Multilinguality Capabilities of OSF

From OSF Wiki
Jump to: navigation, search

Introduction

This document outlines all of the current multiple language capabilities of OSF. It discusses how new languages can be supported by OSF. It discusses the language capabilities of specific OSF Web Services endpoints. And it finally explains how the content that is added to OSF may be described with language markup.

UTF-8 Encoding

The information manipulated by OSF endpoints are encoded in UTF-8. This means that all of the records that are created into OSF should be encoded in UTF-8, and that all records returned by any web service endpoints are also encoded in UTF-8. All record information transmitted between OSF endpoints and the Virtuoso and Solr servers are also transmitted using UTF-8 encoding.

Because of the universal use of UTF-8 encoding within OSF, please take care to encode all language variants with UTF-8. Not following this rule will cause unexpected behavior.

Specifying Language Tags

Language tags are used to indicate the language of text as specified below. These language tags use the equivalent of the xml:lang attribute for XML, the particular codes for which are defined in the IETF's BCP 47. (BCP stands for 'Best Current Practice', and is a persistent name for a series of RFCs whose numbers change as they are updated.) This document provides multiple tag options for various languages. Use the two-letter designator variant wherever possible (such as 'en' for English, 'fr' for French, 'de' for German, etc.) The full list of these codes may be found in the IANA registry.

For additional information about language codes and tags in semantic documents, see the W3C's Language tags in HTML and XML.

Multiple Language Capabilities of Endpoints

In this section, we cover all OSF Web Services endpoints that have multilingual capabilities. We will explain how they work, how they should be used, and what users should expect when using these endpoints.

If a OSF Web Service instance is not configured for multilinguality, then it means that the default language being used is 'en' (English). Under this default condition, all property strings (literal values) used in OSF records are understood to be English strings or literals.

CRUD: Create

The CRUD: Create web service endpoint is used to create new content into OSF. All of the content that is created by this web service endpoint is serialized into RDF/XML or RDF/N3. Depending on how the RDF data is defined, three different behaviors may occur:

  1. When indexing content in Virtuoso:
    1. If no language is specified for a literal value, then:
      1. No specific language information is indexed in Virtuoso
    2. If a language is specified for a literal value, but this language is not configured in OSF, then:
      1. The language tag defined in the input RDF document will be indexed in Virtuoso (even if not supported)
    3. If a language is specified for a literal value and this language is configured in OSF, then:
      1. The language tag defined in the input RDF document will be indexed in Virtuoso
  2. When indexing content in Solr:
    1. If no language is specified for a literal value, then:
      1. The CRUD: Create endpoint stores this literal using the default language, which is 'en' (English)
    2. If a language is specified for a literal value, but this language is not configured in OSF, then:
      1. The CRUD: Create endpoint stores this literal using the default language, which is 'en' (English)
    3. If a language is specified for a literal value and this language is configured in OSF, then:
      1. The CRUD: Create endpoint properly stores and indexes this literal using the specified language.

What is important to note with these different behavior depending if the RDF document is indexed in Virtuoso or Solr is that everything that is indexed in Virtuoso is a faithful representation of what got indexed. This means that even if the languages are not supported, that information will be indexed in Virtuoso. That way, if the languages become supported in the future, we will be able to use the DMT (Datasets Management Tool) tool to re-index the content in Solr that is existing in Virtuoso to properly populate the updated Solr schema index.

Once the proper usecase is detected, then the endpoint will take care to properly index that information into the underlying data management systems, namely Virtuoso and Solr.

This means that what drives the multilingual capabilities of this CRUD: Create endpoint is the way the input RDF data is described. There are no input parameters related to multiliguality for this endpoint.

Note: read the section "How to Describe RDF Data for Multilinguality" below to know how to write your RDF document to transmit to the CRUD: Create endpoint to enable multilinguality.

Here is an example of such a RDF/XML document that uses two different languages for some of its literals descriptions:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>
    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

CRUD: Update

The CRUD: Update web service endpoint is used to update existing content in OSF. All of the content that is being updated by this web service endpoint is serialized into RDF/XML or RDF/N3. Depending how the RDF data that is being updated by this endpoint is defined, three different behaviors may occur. These are the same behavior as the CRUD: Create web service endpoint we described above:

  1. When indexing content in Virtuoso:
    1. If no language is specified for a literal value, then:
      1. No specific language information is indexed in Virtuoso
    2. If a language is specified for a literal value, but this language is not configured in OSF, then:
      1. The language tag defined in the input RDF document is indexed in Virtuoso (even if not supported)
    3. If a language is specified for a literal value and this language is configured in OSF, then:
      1. The language tag defined in the input RDF document is indexed in Virtuoso
  2. When indexing content in Solr:
    1. If no language is specified for a literal value, then:
      1. The CRUD: Update endpoint stores this literal using the default language, which is 'en' (English)
    2. If a language is specified for a literal value, but this language is not configured in OSF, then:
      1. The CRUD: Update endpoint stores the literal using the default language, which is 'en' (English)
    3. If a language is specified for a literal value and this language is configured in OSF, then:
      1. The CRUD: Update endpoint properly indexes this literal using the specified language.

What is important to notice with these different behaviors depending if the RDF document is indexed in Virtuoso or Solr is that everything that is indexed in Virtuoso is a faithful representation of what got indexed. This means that even if the languages are not supported, that information will be indexed in Virtuoso. That way, if the languages become supported in the future, we will be able to use the DMT (Datasets Management Tool) tool to re-index the content in Solr that is existing in Virtuoso to properly populate the updated Solr schema index.

Once the proper usecase is detected, then the endpoint will take care to properly update that information into the underlying data management systems, namely Virtuoso and Solr.

This means that what drives the multilingual capabilities of this CRUD: Update endpoint is the way the input RDF data is described. There are no input parameters related to multiliguality for this endpoint.

Note: read the section "How to Describe RDF Data for Multilinguality" below to know how to write your RDF document to transmit to the CRUD: Create endpoint to enable multilinguality.

CRUD: Read

The CRUD: Read web service endpoint is used to read content from OSF. Different multilinguality behavior exists within the CRUD: Read web service endpoint depending how the RDF data that is currently indexed in OSF has been described. If the lang parameter of the web service endpoint is omitted, then the default language is used, which is 'en' (English). Let's describe the different behaviors of the CRUD: Read web service endpoint depending on what is specified with the lang parameter, which determines what is indexed in OSF.

Here are the different behaviors that may occur with the CRUD: Read web service endpoint, depending on how it is being used (input parameters) and how the data is being described in OSF:

  1. If the input parameter lang is not specified for the query, then:
    1. This means that the default 'en' language will be used by the endpoint
    2. The endpoint will then return:
      1. all of the triples where the value is a URI
      2. all of the triples where the literal values are defined to be using the language 'en' (English)
      3. all of the triples where the literal values have no defined languages (this means that the language string was not specified when the record got indexed using CRUD: Read)
  2. If the input parameter lang is specified with 'fr' (French) (for this example) for the query, then:
    1. The endpoint will then return:
      1. all of the triples where the value is a URI
      2. all of the triples where the literal values are defined to be using the language 'fr' (French)
      3. all of the triples where the literal values have no defined languages (this means that the language string was not specified when the record got indexed using CRUD: Read).

Now, let's check a few examples of what will be returned depending on the CRUD: Read requests that are being sent, and what is indexed in OSF:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>
    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

In the above RDF/XML document, we can see that we have a iron:prefLabel property defined with an English value, and another one with a French value. Now let's see what is the RDF/XML document looks like that will be returned by the CRUD: Read endpoint depending on these input parameters:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>
Since the lang parameter is unspecified, 'en' (English) is being used by the endpoint. That means that the English values are returned, along with the values that have no language specified for them. Now let's see what happens when we define the language parameter for the French language:
<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>
Since the lang parameter 'fr' (French) is being used by the endpoint, the French strings are returned. Also, given the behavior outlined at the beginning of this section, all the values without any language specified are returned as well.
Behavior of literals without any languages specified


Note that the behaviour of the literals that have no languages specified for them can easily change by updating the CRUD: Read web service endpoint code. However, it is the current behaviour of the endpoint for this usecase.


Ontology: Read

The Ontology: Read web service endpoint is used to read ontology Classes, Properties and Named Individuals content from the defined ontologies in OSF. Exactly the same behavior as the CRUD: Read endpoint discussed above applies here as well.

Search

The Search web service endpoint is used to read content in OSF. Different multilinguality behaviour exists within the Search web service endpoint depending how the RDF data that is currently indexed in OSF as been described. If the lang parameter of the web service endpoint is omitted, then the default language is used, which is 'en' (English). Let's describe the different behaviours of the Search web service endpoint depending what is specified with the lang parameter, and depending what is indexed in OSF.

Here are the different behaviours that may happens with the Search web service endpoint, depending on how it is being used (input parameters) and how the data is being described in OSF:

  1. If the input parameter lang is not specified for the query, then:
    1. This means that the default 'en' language will be used by the endpoint
    2. The endpoint will then return:
      1. all of the triples where the value is a URI
      2. all of the triples where the literal values are defined to be using the language 'en' (English)
      3. all of the triples where the literal values have no defined languages (this means that the language string was not specified when the record got indexed using Search)
  2. If the input parameter lang is specified with 'fr' (French) for the query, then:
    1. The endpoint will then return:
      1. all of the triples where the value is a URI
      2. all of the triples where the literal values are defined to be using the language 'fr' (French)

Now, let's check a few examples of what will be returned depending on the Search requests that are being sent, and what is indexed in OSF:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>
    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <iron:description>Some description</iron:description>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

In the above RDF/XML document, we can see that we have a iron:prefLabel property defined with an English value, and another one with a French value. Then we have a iron:description where its value is not specified with any particular language string. Now let's see what is the RDF/XML document that will be returned by the Search endpoint depending on the input parameters:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>

    <iron:description>Some description</iron:description>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

Since the lang parameter is unspecified, 'en' (English) is being used by the endpoint. This default means that the English values are returned, along with the values that have no language specified for them. Now let's see what happens when we define the language parameter for the French language:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

Since the lang parameter 'fr' (French) is being used by the endpoint, only the French strings are returned.

SPARQL

The SPARQL web service endpoint is used to read content from OSF using SPARQL queries. Different multilingual behaviour exists within the Search web service endpoint depending how the RDF data that is currently indexed in OSF has been described. The way to get the language tag of a literal value is by properly creating your SPARQL queries. In this section, we will see how we can create SPARQL queries to get the description of the records with their language definition.

Let's take this input RDF that will be indexed into OSF:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://localhost/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>
    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <iron:description>Some description</iron:description>

    <foaf:thumbnail>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://localhost/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

The only mimes supported by the SPARQL endpoint that will handle returning the language definition of a literal value are:

  1. application/sparql-results+xml (SPARQL resultset in XML)
  2. application/sparql-results+json (SPARQL resultset in JSON)

Then, the kind of SPARQL query that we have to create to get the language definition of a literal value is:

select ?p ?o (LANG(?o)) as ?olang (DATATYPE(?o)) as ?otype
where
{
  <http://localhost/datasets/HistImages/histImage_5> ?p ?o.
}
What will be returned by such a query, if the application/sparql-results-xml is being used, is:
<sparql xmlns="http://www.w3.org/2005/sparql-results#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3.org/2001/sw/DataAccess/rf1/result2.xsd">
 <head>
  <variable name="p"/>
  <variable name="o"/>
  <variable name="olang"/>
  <variable name="otype"/>
 </head>
 <results distinct="false" ordered="true">
  <result>
   <binding name="p"><uri>http://www.w3.org/1999/02/22-rdf-syntax-ns#type</uri></binding>
   <binding name="o"><uri>http://xmlns.com/foaf/0.1/Image</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://xmlns.com/foaf/0.1/thumbnail</uri></binding>
   <binding name="o"><literal>http://localhost/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</literal></binding>
   <binding name="olang"><literal></literal></binding>
   <binding name="otype"><uri>http://www.w3.org/2001/XMLSchema#string</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://xmlns.com/foaf/0.1/page</uri></binding>
   <binding name="o"><literal>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</literal></binding>
   <binding name="olang"><literal></literal></binding>
   <binding name="otype"><uri>http://www.w3.org/2001/XMLSchema#string</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://xmlns.com/foaf/0.1/img</uri></binding>
   <binding name="o"><literal>http://localhost/files/images-datasets/iowa_city_depot.jpg</literal></binding>
   <binding name="olang"><literal></literal></binding>
   <binding name="otype"><uri>http://www.w3.org/2001/XMLSchema#string</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://xmlns.com/foaf/0.1/topic</uri></binding>
   <binding name="o"><uri>http://purl.org/ontology/muni#Historic_buildings</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://xmlns.com/foaf/0.1/topic</uri></binding>
   <binding name="o"><uri>http://purl.org/ontology/muni#Railway_stations</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://purl.org/ontology/iron#prefLabel</uri></binding>
   <binding name="o"><literal xml:lang="en">Iowa City Old Depot</literal></binding>
   <binding name="olang"><literal>en</literal></binding>
   <binding name="otype"><uri>http://www.w3.org/2001/XMLSchema#string</uri></binding>
  </result>
  <result>
   <binding name="p"><uri>http://purl.org/ontology/iron#prefLabel</uri></binding>
   <binding name="o"><literal xml:lang="fr">Iowa City Vieux Dépôt</literal></binding>
   <binding name="olang"><literal>fr</literal></binding>
   <binding name="otype"><uri>http://www.w3.org/2001/XMLSchema#string</uri></binding>
  </result>
 </results>
</sparql>
As you can notice, if no language tag is defined in the input RDF then this binding will be displayed: <binding name="olang"><literal></literal></binding>. However, if there is one, then this kind of binding will be displayed: <binding name="olang"><literal>en</literal></binding> or <binding name="olang"><literal>fr</literal></binding>

How to Configure the OSF Web Services for Multilinguality

This section explains how to configure the various pieces in a OSF instance in order to properly provide multilingual support. There are basically two pieces to configure: OSF Web Services and Solr.

How to Configure the OSF Web Services for Multilinguality

If you want to support more than one language with the OSF Web Services endpoints, then you have to properly configure OSF Web Services to support more than one distinct language.

Configuring OSF Web Services to support a new language is really easy and straightforward. The only thing that is required is to edit the osf.ini configuration file, and to add the new language strings into the configuration. The osf.ini file configured to support both English and French languages is defined like this:

[lang]
; An array of supported languages by the OSF Web Services instance.
; Each of the language that appear here have to be properly
; configured in the Solr schema.
 
supported_languages[] = "en"
supported_languages[] = "fr"

You can easily add new language just by adding them to that array.

You can add a new language at any time. The only thing you have to do is to change this configuration, then make sure you properly describe the RDF documents you are indexing using this new language string. The real impact is on the Solr configuration that we will see below. Other than that, languages can be added and removed anytime in the osf.ini configuration file.

How to Configure Solr for Multilinguality

Supporting more languages in OSF Web Services does have big impacts on the Solr schema used by a OSF Web Services instance. The method used to support multiple languages for the same record is simple:

  • A new set of fields is created in the Solr schema for that new language we want to support

The method is simple, but the impact on the schema.xml file is big.

What this means is that for all and every Schema fields that have a language suffix "_en", you have to duplicate that field to support a new language. Let's take that Solr schema.xml file that currently only supports the English language:

<fields>
   <!-- Unique ID of the Solr document across any solr core and/or shards -->
   <field name="uid" type="string" indexed="true" stored="true" required="true" multiValued="false" />
   
   <!-- URI of the record represented by this Solr document -->
   <field name="uri" type="string" indexed="true" stored="true" required="true" multiValued="false" />
   
   <!-- Dataset provenance of the record -->
   <field name="dataset" type="string" indexed="true" stored="true" required="true" multiValued="false" />
 
   <!-- Types of the record -->
   <field name="type" type="string" indexed="true" stored="true" multiValued="true"/>
   <field name="type_single_valued" type="string" indexed="true" stored="false" multiValued="false"/>
   
   <!-- Inferred types of the record -->
   <field name="inferred_type" type="string" indexed="true" stored="true" multiValued="true"/>
   
   <!-- English preferred label of the record -->
   <field name="prefLabel_en" type="text_en" indexed="true" stored="true" multiValued="false"/>
   
   <!-- Preferred label of the record, in English, and unmodified for autocompletion purposes -->
   <field name="prefLabelAutocompletion_en" type="lowercase" indexed="true" stored="false" multiValued="false"/>
   
   <!-- Alternative labels of the record, in English -->
   <field name="altLabel_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
   
   <!-- Short description of the record, in English -->
   <field name="description_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
   
   <!-- Preferred URL of a webpage that talks about this record, or gives additional information about it -->
   <field name="prefURL" type="string" indexed="true" stored="true" multiValued="false"/>
   
   <!-- Used to create attribute/key pairs that were not previously created in this schema -->
   
   <!-- Value of the attribute defined by the dynamic field -->
   <dynamicField name="*_attr_en" type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_en_single_valued" type="text_en" indexed="true" stored="true" multiValued="false"/>  
   <!-- These dynamic fields are used when the datatype of a value of a property is
       specified. That way, we are able to perform special filters, based on the
   data types such as dates. -->
   <dynamicField name="*_attr_date"  type="date" indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_date_single_valued" type="date" indexed="true" stored="true" multiValued="false"/>
   <dynamicField name="*_attr_float"  type="float" indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_float_single_valued" type="float" indexed="true" stored="true" multiValued="false"/>
   <dynamicField name="*_attr_int"  type="int" indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_int_single_valued" type="int" indexed="true" stored="true" multiValued="false"/>
   
   <!-- Label (value) representing the record referenced by its URI (only used if the
       value of that property is a URI: a reference to another record -->
   <dynamicField name="*_attr_obj_en"  type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_obj_en_single_valued"  type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   
   <!-- URI value of the attribute defined by the dynamic field -->
   <dynamicField name="*_attr_obj_uri"  type="string"  indexed="true"  stored="true" multiValued="true"/>  
   <!-- Unmodified version of the values of the attributes used for faceting purposes -->
   <dynamicField name="*_attr_facets"  type="string"  indexed="true"  stored="false" multiValued="true"/>    
   
   <!-- Field used to keep the correspondance between the URI and the Label of the URI when using the "uriliteral"
       type for the aggregate_attributes_object_type Search parameter -->
   <dynamicField name="*_attr_uri_label_facets"  type="string"  indexed="true"  stored="false" multiValued="true"/>    
   
   <!-- Reified statements for this record -->
   <dynamicField name="*_reify_attr"  type="string"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_reify_obj"  type="string"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_reify_value_en"  type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   
   <!-- This field is use to keep track of the dynamicFields that have been created.
       It is the way to be able to get the counts of properties from facets -->
   <field name="attribute" type="string" indexed="true" stored="true" multiValued="true"/>
   <!-- All text, from all properties, used when we search over all properties.
       The default language supported by a OSF Web Services instance is English. If
       other languages are supported, then you have to create a new "all_text_xyz"
       field for these new supported languages where "xyz" is the code of the
       language. -->
   <field name="all_text_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
   <!-- Geo-Related Fields -->
   <field name="lat" type="tdouble" indexed="true" stored="true" multiValued="true" />
   <field name="long" type="tdouble" indexed="true" stored="true" multiValued="true" />
   <field name="geohash" type="geohash" indexed="true" stored="true" multiValued="true"/>
   <field name="alt" type="tfloat" indexed="true" stored="true" multiValued="true" />
   <field name="polygonCoordinates" type="string" indexed="true" stored="true" multiValued="true" />
   <field name="polylineCoordinates" type="string" indexed="true" stored="true" multiValued="true" />
   <field name="located_in" type="string" indexed="true" stored="true" multiValued="true" />
   
 </fields>
 <!-- Field to use to determine and enforce document uniqueness.
     Unless this field is marked with required="false", it will be a required field
  -->
 <uniqueKey>uid</uniqueKey>
 <!-- Copy all values of all fields to the "all_text" field. This is needed to be able to query
    all data from all dynamic properties -->
 <copyField source="*_attr_en"  dest="all_text_en" />
 <copyField source="*_attr_obj_en"  dest="all_text_en" />
 <copyField source="*_reify_value_en"  dest="all_text_en" />
 <copyField source="prefLabel_en"  dest="all_text_en" />
 <copyField source="altLabel_en"  dest="all_text_en" />
 <copyField source="description_en"  dest="all_text_en" />
 
 <copyField source="*_attr_date"  dest="all_text_en" />
 <copyField source="*_attr_float"  dest="all_text_en" />
 <copyField source="*_attr_int"  dest="all_text_en" />
 
 <copyField source="prefURL"  dest="all_text_en" />
 <copyField source="located_in"  dest="all_text_en" />
 <copyField source="lat"  dest="all_text_en" />
 <copyField source="long"  dest="all_text_en" />
 <copyField source="geohash"  dest="all_text_en" />
 
 <!-- Note: for each subsequent languages handled by the OSF Web Services
     instance, you have to copyField the above fields as well, and
   not just the one with the suffix "_en" -->
 
 <!-- field for the QueryParser to use when an explicit fieldname is absent -->
 <defaultSearchField>all_text_en</defaultSearchField>
 
 <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
 <solrQueryParser defaultOperator="AND"/>
Now, let's extend it to support a new language, namely the French language. What we have to do is to create a series of new fields with the "_fr" suffix like this:
<fields>
   <!-- Unique ID of the Solr document across any solr core and/or shards -->
   <field name="uid" type="string" indexed="true" stored="true" required="true" multiValued="false" />
   
   <!-- URI of the record represented by this Solr document -->
   <field name="uri" type="string" indexed="true" stored="true" required="true" multiValued="false" />
   
   <!-- Dataset provenance of the record -->
   <field name="dataset" type="string" indexed="true" stored="true" required="true" multiValued="false" />
 
   <!-- Types of the record -->
   <field name="type" type="string" indexed="true" stored="true" multiValued="true"/>
   <field name="type_single_valued" type="string" indexed="true" stored="false" multiValued="false"/>
   
   <!-- Inferred types of the record -->
   <field name="inferred_type" type="string" indexed="true" stored="true" multiValued="true"/>
   
   <!-- English preferred label of the record -->
   <field name="prefLabel_en" type="text_en" indexed="true" stored="true" multiValued="false"/>
   <field name="prefLabel_fr" type="text_fr" indexed="true" stored="true" multiValued="false"/>
   
   <!-- Preferred label of the record, in English, and unmodified for autocompletion purposes -->
   <field name="prefLabelAutocompletion_en" type="lowercase" indexed="true" stored="false" multiValued="false"/>
   <field name="prefLabelAutocompletion_fr" type="lowercase" indexed="true" stored="false" multiValued="false"/>
   
   <!-- Alternative labels of the record, in English -->
   <field name="altLabel_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
   <field name="altLabel_fr" type="text_fr" indexed="true" stored="true" multiValued="true"/>
   
   <!-- Short description of the record, in English -->
   <field name="description_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
   <field name="description_fr" type="text_fr" indexed="true" stored="true" multiValued="true"/>
   
   <!-- Preferred URL of a webpage that talks about this record, or gives additional information about it -->
   <field name="prefURL" type="string" indexed="true" stored="true" multiValued="false"/>
   
   <!-- Used to create attribute/key pairs that were not previously created in this schema -->
   
   <!-- Value of the attribute defined by the dynamic field -->
   <dynamicField name="*_attr_en" type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_fr" type="text_fr"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_en_single_valued" type="text_en" indexed="true" stored="true" multiValued="false"/>  
   <dynamicField name="*_attr_fr_single_valued" type="text_fr" indexed="true" stored="true" multiValued="false"/>  
   <!-- These dynamic fields are used when the datatype of a value of a property is
       specified. That way, we are able to perform special filters, based on the
   data types such as dates. -->
   <dynamicField name="*_attr_date"  type="date" indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_date_single_valued" type="date" indexed="true" stored="true" multiValued="false"/>
   <dynamicField name="*_attr_float"  type="float" indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_float_single_valued" type="float" indexed="true" stored="true" multiValued="false"/>
   <dynamicField name="*_attr_int"  type="int" indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_int_single_valued" type="int" indexed="true" stored="true" multiValued="false"/>
   
   <!-- Label (value) representing the record referenced by its URI (only used if the
       value of that property is a URI: a reference to another record -->
   <dynamicField name="*_attr_obj_en"  type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_obj_fr"  type="text_fr"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_obj_en_single_valued"  type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_attr_obj_fr_single_valued"  type="text_fr"  indexed="true"  stored="true" multiValued="true"/>  
   
   <!-- URI value of the attribute defined by the dynamic field -->
   <dynamicField name="*_attr_obj_uri"  type="string"  indexed="true"  stored="true" multiValued="true"/>  
   <!-- Unmodified version of the values of the attributes used for faceting purposes -->
   <dynamicField name="*_attr_facets"  type="string"  indexed="true"  stored="false" multiValued="true"/>    
   
   <!-- Field used to keep the correspondance between the URI and the Label of the URI when using the "uriliteral"
       type for the aggregate_attributes_object_type Search parameter -->
   <dynamicField name="*_attr_uri_label_facets"  type="string"  indexed="true"  stored="false" multiValued="true"/>    
   
   <!-- Reified statements for this record -->
   <dynamicField name="*_reify_attr"  type="string"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_reify_obj"  type="string"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_reify_value_en"  type="text_en"  indexed="true"  stored="true" multiValued="true"/>  
   <dynamicField name="*_reify_value_fr"  type="text_fr"  indexed="true"  stored="true" multiValued="true"/>  
   
   <!-- This field is use to keep track of the dynamicFields that have been created.
       It is the way to be able to get the counts of properties from facets -->
   <field name="attribute" type="string" indexed="true" stored="true" multiValued="true"/>
   <!-- All text, from all properties, used when we search over all properties.
       The default language supported by a OSF Web Services instance is English. If
       other languages are supported, then you have to create a new "all_text_xyz"
       field for these new supported languages where "xyz" is the code of the
       language. -->
   <field name="all_text_en" type="text_en" indexed="true" stored="true" multiValued="true"/>
   <field name="all_text_fr" type="text_fr" indexed="true" stored="true" multiValued="true"/>
 
   <!-- Geo-Related Fields -->
   <field name="lat" type="tdouble" indexed="true" stored="true" multiValued="true" />
   <field name="long" type="tdouble" indexed="true" stored="true" multiValued="true" />
   <field name="geohash" type="geohash" indexed="true" stored="true" multiValued="true"/>
   <field name="alt" type="tfloat" indexed="true" stored="true" multiValued="true" />
   <field name="polygonCoordinates" type="string" indexed="true" stored="true" multiValued="true" />
   <field name="polylineCoordinates" type="string" indexed="true" stored="true" multiValued="true" />
   <field name="located_in" type="string" indexed="true" stored="true" multiValued="true" />
   
 </fields>
 <!-- Field to use to determine and enforce document uniqueness.
     Unless this field is marked with required="false", it will be a required field
  -->
 <uniqueKey>uid</uniqueKey>
 <!-- Copy all values of all fields to the "all_text" field. This is needed to be able to query
    all data from all dynamic properties -->
 <copyField source="*_attr_en"  dest="all_text_en" />
 <copyField source="*_attr_obj_en"  dest="all_text_en" />
 <copyField source="*_reify_value_en"  dest="all_text_en" />
 <copyField source="prefLabel_en"  dest="all_text_en" />
 <copyField source="altLabel_en"  dest="all_text_en" />
 <copyField source="description_en"  dest="all_text_en" />
 <copyField source="*_attr_fr"  dest="all_text_fr" />
 <copyField source="*_attr_obj_fr"  dest="all_text_fr" />
 <copyField source="*_reify_value_fr"  dest="all_text_fr" />
 <copyField source="prefLabel_fr"  dest="all_text_fr" />
 <copyField source="altLabel_fr"  dest="all_text_fr" />
 <copyField source="description_fr"  dest="all_text_fr" />
 
 <copyField source="*_attr_date"  dest="all_text_en" />
 <copyField source="*_attr_float"  dest="all_text_en" />
 <copyField source="*_attr_int"  dest="all_text_en" />
 
 <copyField source="prefURL"  dest="all_text_en" />
 <copyField source="located_in"  dest="all_text_en" />
 <copyField source="lat"  dest="all_text_en" />
 <copyField source="long"  dest="all_text_en" />
 <copyField source="geohash"  dest="all_text_en" />
 
 <!-- Note: for each subsequent languages handled by the OSF Web Services
     instance, you have to copyField the above fields as well, and
   not just the one with the suffix "_en" -->
 
 <!-- field for the QueryParser to use when an explicit fieldname is absent -->
 <defaultSearchField>all_text_en</defaultSearchField>
 
 <!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
 <solrQueryParser defaultOperator="AND"/>
Once this new schema is in place and used by the running Solr instance(s), then every time CRUD: Create, CRUD: Update, Ontology: Create or Ontology: Update will be used, then this language will be handled and properly indexed into Solr.
Misconfiguration issue


If you added a new supported language in the osf.ini file without modifying Solr's schema.xml file, and if you use one of the web service endpoints mentioned above, then you will get some Solr error telling you that a undefined field is being used in the Solr query when indexing new content into Solr.


Once these two configurations are in place, then you are ready to index new content that uses this newly configured language file. Then all the web service endpoints outlined above will work with that newly added language.

How to Describe RDF Data for Multilinguality

This section explains how the language for the literals contained in a record description can be serialized in RDF+XML or RDF+N3 formats.

If you properly configured the OSF Web Services instance (and the Solr schema) to support the languages you will be using in the RDF files, then you won't have anything special to do other than specifying the language used for each literal value defined in a RDF document. Then, OSF Web Services will properly index all the language related information in both Solr and Virtuoso, and all the endpoints outlined above will behave the way we outlined in this document.

However, what happens if you define language tags that are not supported by the OSF Web Services instance in the RDF files you are indexing? Nothing untoward happens, but the following behavior will happen:

  1. Virtuoso indexes all of the language related information for all the language strings (supported or not)
  2. Solr indexes the unsupported language as if it uses the default language
  3. If you try to use a unsupported language for the lang parameter of the web services, then an unsupported language error will be returned by the endpoint.

RDF+XML

In this section, we will see how we can define the language for specific literals. Let's take this initial RDF file serialized in XML:

<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://ec2-54-237-65-111.compute-1.amazonaws.com/datasets/HistImages/histImage_5">

    <iron:prefLabel>Iowa City Old Depot</iron:prefLabel>
    <iron:prefLabel>Iowa City Vieux Dépôt</iron:prefLabel>

    <iron:description>Some description</iron:description>

    <foaf:thumbnail>http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>
Initially, no language related markup is used in this RDF+XML file for describing this record. However, language specific tagging can easily be added in this serialization using the xml:lang XML attribute. It can be done this way:
<?xml version="1.0"?>
<rdf:RDF xmlns:owl="http://www.w3.org/2002/07/owl#"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
        xmlns:iron="http://purl.org/ontology/iron#"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
        xmlns:wsf="http://purl.org/ontology/wsf#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dcterms="http://purl.org/dc/terms/">

  <foaf:Image rdf:about="http://ec2-54-237-65-111.compute-1.amazonaws.com/datasets/HistImages/histImage_5">

    <iron:prefLabel xml:lang="en">Iowa City Old Depot</iron:prefLabel>
    <iron:prefLabel xml:lang="fr">Iowa City Vieux Dépôt</iron:prefLabel>

    <iron:description xml:lang="en">Some description</iron:description>

    <foaf:thumbnail>http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/thumbs/tb_iowa_city_depot.jpg</foaf:thumbnail>
    <foaf:page>http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station</foaf:page>
    <foaf:img>http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/iowa_city_depot.jpg</foaf:img>
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Historic_buildings" />
    <foaf:topic rdf:resource="http://purl.org/ontology/muni#Railway_stations" />
  </foaf:Image>
</rdf:RDF>

As you can see, we used the xml:lang attribute to specify the language that has been used to write each of these literal values. This new information will then be taken into account by OSF Web Services to properly index this information into the different data management systems, namely: Virtuoso and Solr.

RDF+N3

In this section, we see how we can define language for specific literals. Let's take this initial RDF file serialized in N3:

@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix iron: <http://purl.org/ontology/iron#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix wsf: <http://purl.org/ontology/wsf#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix dcterms: <http://purl.org/dc/terms/> .

<http://ec2-54-237-65-111.compute-1.amazonaws.com/datasets/HistImages/histImage_5>
  a foaf:Image ;
 
  iron:prefLabel """Iowa City Old Depot (revision testing)""" ;
  iron:prefLabel """Iowa City Old Depot (revision testing)""" ;

  iron:description """Some descriptions""" ;
 
  foaf:thumbnail """http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/thumbs/tb_iowa_city_depot.jpg""" ;
  foaf:page """http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station""" ;
  foaf:img """http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/iowa_city_depot.jpg""" ;
  foaf:topic <http://purl.org/ontology/muni#Historic_buildings> ;
  foaf:topic <http://purl.org/ontology/muni#Railway_stations> .
Initially, no language related markup is used in this RDF+N3 file for describing this record. However, language specific tagging can easily be added in this serialization using the special N3 markup @lang. It can be done this way:
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix iron: <http://purl.org/ontology/iron#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix wsf: <http://purl.org/ontology/wsf#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix dcterms: <http://purl.org/dc/terms/> .

<http://ec2-54-237-65-111.compute-1.amazonaws.com/datasets/HistImages/histImage_5>
  a foaf:Image ;
 
  iron:prefLabel """Iowa City Old Depot (revision testing)"""@en ;
  iron:prefLabel """Iowa City Old Depot (revision testing)"""@fr ;

  iron:description """Some descriptions"""@en ;
 
  foaf:thumbnail """http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/thumbs/tb_iowa_city_depot.jpg""" ;
  foaf:page """http://en.wikipedia.org/wiki/Chicago,_Rock_Island_and_Pacific_Railroad_Passenger_Station""" ;
  foaf:img """http://ec2-54-237-65-111.compute-1.amazonaws.com/files/images-datasets/iowa_city_depot.jpg""" ;
  foaf:topic <http://purl.org/ontology/muni#Historic_buildings> ;
  foaf:topic <http://purl.org/ontology/muni#Railway_stations> .
As you can see, we used the N3 markup @lang to specify the language that as been used to write each of these literal values. This new information will then be taken into account by OSF Web Services to properly index this information into the different data management systems, namely: Virtuoso and Solr.