Normalising the Capitalisation of Language Tags

The Problem

Most text values in PoolParty have the RDF type "langString", which means they are annotated with a language tag, for example "Taxonomy"@en-US.

In PoolParty versions 6.1 and 6.2, two-part language tags can be generated and stored with inconsistent capitalisation. This is not an issue within PoolParty, since RDF language tags are case-insensitive as required by the applicable standards, but when the data is exported, the inconsistent casing can confuse naive text-processing tools.

This section shows how to normalise the case of language tags for the purpose of exporting data in a consistent form.

For more on language tags and the scope of the problem, see Background: Capitalisation of RDF Language Tags in Graph Databases.

Note

This issue affects PoolParty versions 6.1 and 6.2. In PoolParty 7, language tags are generated with consistent capitalisation, namely in the recommended interchange format, for example @en-US. The issue may also arise when data from external sources, inconsistently capitalised, is imported into PoolParty 6.1 or any later version.

Normalising Language Tag Capitalisation in RDF4J

This section shows how to adjust the capitalisation of language tags through the SPARQL interface. The conversion is hindered by the fact that although RDF4J will store language tags in any case we specify, it does treat the case variants as equivalent.

Consequently it is difficult to persuade it to execute a conversion that in principle changes nothing. Even CONSTRUCT queries (for exporting the data) will ignore attempts to change the case of language tags.

Because RDF4J will not replace a language tag with a case variant of itself, we must do the conversion in two steps:

  1. Query 1 will rewrite, for example, "Taxonomy"@en-us to "Taxonomy"@en-US-x-fixme.

  2. Query 2 will rewrite it again to "Taxonomy"@en-US.

Warning

RDF4J will keep deleted strings in its cache, and will insert the original form instead of the desired one.

To defeat caching from the user interface, you have to restart PoolParty between the two queries. Fortunately this only needs to be done once, no matter how many PoolParty projects you normalise.

Note

This solution only works for two-part tags (language-REGION). Do not use if you have tags with more than two components.

Solution Summary

  1. Run the query casefix-step1.sparql, included below. Commit. Repeat for each project that needs conversion.

  2. Restart PoolParty.

  3. Run query casefix-step2.sparql, included below. Commit. Repeat for each project that needs conversion.

Detailed Instructions

The SPARQL queries below must be run separately on each PoolParty project that needs to be repaired. Because string literals are shared across graphs, it is necessary to process all RDF graphs of the project at once.

  1. Log into PoolParty.

  2. Open the SPARQL shell: expand the Toolsmenu, Admin Scripts, click the link PP SPARQL shell.

  3. From the drop-down Users Repository, select the repository of the PoolParty project to be repaired.

    • For example, if the project is called 'Hello world', find and select 'Project: Hello world'.

  4. Click Connect.

  5. Delete all text from the SPARQL shell form, and paste in the contents of casefix-step1.sparql (see below).

  6. Click Run Query. Ensure that the response is 'Query succeeded'.

  7. Click Commit Updates.

    • If you wish, you may inspect the interim result with the SELECT query shown at the end of these instructions.

  8. If you plan to normalise more than one PoolParty project, connect to each project's repository, execute casefix-step1.sparql, and commit.

    Warning

    Do not run the script twice on the same project.

  9. Shut down the PoolParty server.

    • Shutting down the server clears the cache, allowing the next step to work as intended.

    • This is necessary because the deleted labels with the lowercased language tags are still in the cache, and will be resurrected if we try to insert the same labels with an equivalent (differently capitalised) language tag.

  10. Restart the server and wait until it is fully operational.

  11. Your SPARQL shell session has become invalid now. Click Disconnect, then click Connect to initiate a new connection. (The User Repository selector should still be showing your last project's repository).

  12. Delete everything in the SPARQL shell, and paste in the contents of the query casefix-step2.sparql (see below).

  13. Click Run Query, check that it reports success, and press Commit Updates.

  14. If you have multiple projects to normalise, connect to each project's repository and apply step 2 of the fix.

  15. The conversion is finished. You can use the following query to inspect the result:

inspect-langstrings.sparql

# To inspect your thesaurus only, change ?graph to the uri of your thesaurus graph.
SELECT *
WHERE
{ GRAPH ?graph
  {
    ?s ?p ?o .
    FILTER ( DATATYPE(?o) = rdf:langString && CONTAINS(LANG(?o), "-") ) .
  }
} LIMIT 200

SPARQL Queries to Normalise Capitalisation

casefix-step1.sparql

# Step 1 of the fix: Convert and temporarily tag for distinctness
DELETE
{ GRAPH ?graph { ?s ?p ?o } }
INSERT
{ GRAPH ?graph { ?s ?p ?corrected } }
WHERE
{ GRAPH ?graph
  {
    ?s ?p ?o .
    # Restrict: langStrings with a two-part language tag
    FILTER ( DATATYPE(?o) = rdf:langString && CONTAINS(LANG(?o), "-") ) .

    # Build new language identifier
    BIND(STRBEFORE(LANG(?o), "-") AS ?lgmain)
    BIND(STRAFTER(LANG(?o), "-") AS ?state)
    # Only "fix" it if it is not already capitalised
    FILTER( ?state != UCASE(?state) )
    
    BIND(CONCAT(?lgmain, "-", UCASE(?state), "-x-fixme") AS ?newlang)
    # Rebuild the langString
    BIND(STRLANG(STR(?o), ?newlang) AS ?corrected)
  }
}

The PoolParty server must be restarted between the two queries. Detailed instructions are given above.

casefix-step2.sparql

# Step 2 of the fix: Remove the temporary tags.
DELETE
{ GRAPH ?graph { ?s ?p ?o } }
INSERT
{ GRAPH ?graph { ?s ?p ?corrected } }
WHERE
{ GRAPH ?graph
  {
    ?s ?p ?o .
    # Restrict: langStrings with a two-part language tag
    FILTER ( DATATYPE(?o) = rdf:langString && CONTAINS(LANG(?o), "-x-fixme") ) .

    # Build new language identifier
    BIND(STRBEFORE(LANG(?o), "-x-fixme") AS ?real)
    BIND(STRLANG(STR(?o), ?real) AS ?corrected)
  }
}

Results

After running the queries in the previous section, PoolParty's graph database contains literals with consistently capitalised language tags that can be exported in the usual way.

Note

The continued use of PoolParty, versions 6.1 or 6.2, may introduce more of the inconsistently capitalised language tags. You can avoid this problem by upgrading to PoolParty 7, which uses the conventional capitalisation for all new language tags.