@@ -88,8 +88,18 @@ export class Literal extends Term {
8888 // Find the last quotation mark (e.g., '"abc"@en-us')
8989 const id = this . id ;
9090 let atPos = id . lastIndexOf ( '"' ) + 1 ;
91+ const dirPos = id . lastIndexOf ( '--' ) ;
9192 // If "@" it follows, return the remaining substring; empty otherwise
92- return atPos < id . length && id [ atPos ++ ] === '@' ? id . substr ( atPos ) . toLowerCase ( ) : '' ;
93+ return atPos < id . length && id [ atPos ++ ] === '@' ? ( dirPos > atPos ? id . substr ( 0 , dirPos ) : id ) . substr ( atPos ) . toLowerCase ( ) : '' ;
94+ }
95+
96+ // ### The direction of this literal
97+ get direction ( ) {
98+ // Find the last double dash after the closing quote (e.g., '"abc"@en-us--ltr')
99+ const id = this . id ;
100+ const endPos = id . lastIndexOf ( '"' ) ;
101+ const dirPos = id . lastIndexOf ( '--' ) ;
102+ return dirPos > endPos && dirPos + 2 < id . length ? id . substr ( dirPos + 2 ) . toLowerCase ( ) : '' ;
93103 }
94104
95105 // ### The datatype IRI of this literal
@@ -104,8 +114,8 @@ export class Literal extends Term {
104114 const char = dtPos < id . length ? id [ dtPos ] : '' ;
105115 // If "^" it follows, return the remaining substring
106116 return char === '^' ? id . substr ( dtPos + 2 ) :
107- // If "@" follows, return rdf:langString; xsd:string otherwise
108- ( char !== '@' ? xsd . string : rdf . langString ) ;
117+ // If "@" follows, return rdf:langString or rdf:dirLangString ; xsd:string otherwise
118+ ( char !== '@' ? xsd . string : ( id . indexOf ( '--' , dtPos ) > 0 ? rdf . dirLangString : rdf . langString ) ) ;
109119 }
110120
111121 // ### Returns whether this object represents the same term as the other
@@ -119,14 +129,16 @@ export class Literal extends Term {
119129 this . termType === other . termType &&
120130 this . value === other . value &&
121131 this . language === other . language &&
132+ ( ( this . direction === other . direction ) || ( this . direction === '' && ! other . direction ) ) &&
122133 this . datatype . value === other . datatype . value ;
123134 }
124135
125136 toJSON ( ) {
126137 return {
127- termType : this . termType ,
128- value : this . value ,
129- language : this . language ,
138+ termType : this . termType ,
139+ value : this . value ,
140+ language : this . language ,
141+ direction : this . direction ,
130142 datatype : { termType : 'NamedNode' , value : this . datatypeString } ,
131143 } ;
132144 }
@@ -216,9 +228,22 @@ export function termFromId(id, factory, nested) {
216228 return factory . literal ( id . substr ( 1 , id . length - 2 ) ) ;
217229 // Literal with datatype or language
218230 const endPos = id . lastIndexOf ( '"' , id . length - 1 ) ;
231+ let languageOrDatatype ;
232+ if ( id [ endPos + 1 ] === '@' ) {
233+ languageOrDatatype = id . substr ( endPos + 2 ) ;
234+ const dashDashIndex = languageOrDatatype . lastIndexOf ( '--' ) ;
235+ if ( dashDashIndex > 0 && dashDashIndex < languageOrDatatype . length ) {
236+ languageOrDatatype = {
237+ language : languageOrDatatype . substr ( 0 , dashDashIndex ) ,
238+ direction : languageOrDatatype . substr ( dashDashIndex + 2 ) ,
239+ } ;
240+ }
241+ }
242+ else {
243+ languageOrDatatype = factory . namedNode ( id . substr ( endPos + 3 ) ) ;
244+ }
219245 return factory . literal ( id . substr ( 1 , endPos - 1 ) ,
220- id [ endPos + 1 ] === '@' ? id . substr ( endPos + 2 )
221- : factory . namedNode ( id . substr ( endPos + 3 ) ) ) ;
246+ languageOrDatatype ) ;
222247 case '[' :
223248 id = JSON . parse ( id ) ;
224249 break ;
@@ -255,7 +280,7 @@ export function termToId(term, nested) {
255280 case 'Variable' : return `?${ term . value } ` ;
256281 case 'DefaultGraph' : return '' ;
257282 case 'Literal' : return `"${ term . value } "${
258- term . language ? `@${ term . language } ` :
283+ term . language ? `@${ term . language } ${ term . direction ? `-- ${ term . direction } ` : '' } ` :
259284 ( term . datatype && term . datatype . value !== xsd . string ? `^^${ term . datatype . value } ` : '' ) } `;
260285 case 'Quad' :
261286 const res = [
@@ -350,6 +375,11 @@ function literal(value, languageOrDataType) {
350375 if ( typeof languageOrDataType === 'string' )
351376 return new Literal ( `"${ value } "@${ languageOrDataType . toLowerCase ( ) } ` ) ;
352377
378+ // Create a language-tagged string with base direction
379+ if ( languageOrDataType !== undefined && ! ( 'termType' in languageOrDataType ) ) {
380+ return new Literal ( `"${ value } "@${ languageOrDataType . language . toLowerCase ( ) } ${ languageOrDataType . direction ? `--${ languageOrDataType . direction . toLowerCase ( ) } ` : '' } ` ) ;
381+ }
382+
353383 // Automatically determine datatype for booleans and numbers
354384 let datatype = languageOrDataType ? languageOrDataType . value : '' ;
355385 if ( datatype === '' ) {
0 commit comments