@@ -88,8 +88,17 @@ 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 (e.g., '"abc"@en-us--ltr')
99+ const id = this . id ;
100+ const atPos = id . lastIndexOf ( '--' ) + 2 ;
101+ return atPos > 1 && atPos < id . length ? id . substr ( atPos ) . toLowerCase ( ) : '' ;
93102 }
94103
95104 // ### The datatype IRI of this literal
@@ -104,8 +113,8 @@ export class Literal extends Term {
104113 const char = dtPos < id . length ? id [ dtPos ] : '' ;
105114 // If "^" it follows, return the remaining substring
106115 return char === '^' ? id . substr ( dtPos + 2 ) :
107- // If "@" follows, return rdf:langString; xsd:string otherwise
108- ( char !== '@' ? xsd . string : rdf . langString ) ;
116+ // If "@" follows, return rdf:langString or rdf:dirLangString ; xsd:string otherwise
117+ ( char !== '@' ? xsd . string : ( id . indexOf ( '--' , dtPos ) > 0 ? rdf . dirLangString : rdf . langString ) ) ;
109118 }
110119
111120 // ### Returns whether this object represents the same term as the other
@@ -119,14 +128,16 @@ export class Literal extends Term {
119128 this . termType === other . termType &&
120129 this . value === other . value &&
121130 this . language === other . language &&
131+ ( ( this . direction === other . direction ) || ( this . direction === '' && ! other . direction ) ) &&
122132 this . datatype . value === other . datatype . value ;
123133 }
124134
125135 toJSON ( ) {
126136 return {
127- termType : this . termType ,
128- value : this . value ,
129- language : this . language ,
137+ termType : this . termType ,
138+ value : this . value ,
139+ language : this . language ,
140+ direction : this . direction ,
130141 datatype : { termType : 'NamedNode' , value : this . datatypeString } ,
131142 } ;
132143 }
@@ -216,9 +227,22 @@ export function termFromId(id, factory, nested) {
216227 return factory . literal ( id . substr ( 1 , id . length - 2 ) ) ;
217228 // Literal with datatype or language
218229 const endPos = id . lastIndexOf ( '"' , id . length - 1 ) ;
230+ let languageOrDatatype ;
231+ if ( id [ endPos + 1 ] === '@' ) {
232+ languageOrDatatype = id . substr ( endPos + 2 ) ;
233+ const dashDashIndex = languageOrDatatype . lastIndexOf ( '--' ) ;
234+ if ( dashDashIndex > 0 && dashDashIndex < languageOrDatatype . length ) {
235+ languageOrDatatype = {
236+ language : languageOrDatatype . substr ( 0 , dashDashIndex ) ,
237+ direction : languageOrDatatype . substr ( dashDashIndex + 2 ) ,
238+ } ;
239+ }
240+ }
241+ else {
242+ languageOrDatatype = factory . namedNode ( id . substr ( endPos + 3 ) ) ;
243+ }
219244 return factory . literal ( id . substr ( 1 , endPos - 1 ) ,
220- id [ endPos + 1 ] === '@' ? id . substr ( endPos + 2 )
221- : factory . namedNode ( id . substr ( endPos + 3 ) ) ) ;
245+ languageOrDatatype ) ;
222246 case '[' :
223247 id = JSON . parse ( id ) ;
224248 break ;
@@ -255,7 +279,7 @@ export function termToId(term, nested) {
255279 case 'Variable' : return `?${ term . value } ` ;
256280 case 'DefaultGraph' : return '' ;
257281 case 'Literal' : return `"${ term . value } "${
258- term . language ? `@${ term . language } ` :
282+ term . language ? `@${ term . language } ${ term . direction ? `-- ${ term . direction } ` : '' } ` :
259283 ( term . datatype && term . datatype . value !== xsd . string ? `^^${ term . datatype . value } ` : '' ) } `;
260284 case 'Quad' :
261285 const res = [
@@ -350,6 +374,11 @@ function literal(value, languageOrDataType) {
350374 if ( typeof languageOrDataType === 'string' )
351375 return new Literal ( `"${ value } "@${ languageOrDataType . toLowerCase ( ) } ` ) ;
352376
377+ // Create a language-tagged string with base direction
378+ if ( languageOrDataType !== undefined && ! ( 'termType' in languageOrDataType ) ) {
379+ return new Literal ( `"${ value } "@${ languageOrDataType . language . toLowerCase ( ) } --${ languageOrDataType . direction . toLowerCase ( ) } ` ) ;
380+ }
381+
353382 // Automatically determine datatype for booleans and numbers
354383 let datatype = languageOrDataType ? languageOrDataType . value : '' ;
355384 if ( datatype === '' ) {
0 commit comments