Skip to content

Commit

Permalink
Move LanguageTag and LanguageRange from Hyperspace project
Browse files Browse the repository at this point in the history
Add String>>#escaped and String>>#unescaped to handle character control and Unicode escape sequences
  • Loading branch information
gcotelli committed Jun 19, 2024
1 parent 9df9de6 commit 74f5f3e
Show file tree
Hide file tree
Showing 15 changed files with 1,099 additions and 0 deletions.
17 changes: 17 additions & 0 deletions source/BaselineOfBuoy/BaselineOfBuoy.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ BaselineOfBuoy >> baseline: spec [
baselineComparison: spec;
baselineDynamicBinding: spec;
baselineExceptionHandling: spec;
baselineLocalization: spec;
baselineMath: spec;
baselineMetaprogramming: spec;
baselineSUnit: spec;
Expand Down Expand Up @@ -175,6 +176,22 @@ BaselineOfBuoy >> baselineGS64Development: spec [
group: 'GS64-Development' with: 'Buoy-Chronology-GS64-Extensions'
]

{ #category : 'baselines' }
BaselineOfBuoy >> baselineLocalization: spec [

spec
package: 'Buoy-Localization'
with: [
spec requires:
#( 'Buoy-Assertions' 'Buoy-Dynamic-Binding' 'Buoy-Metaprogramming-Extensions' ) ];
group: 'Deployment' with: 'Buoy-Localization';
package: 'Buoy-Localization-Extensions' with: [ spec requires: 'Buoy-Localization' ];
group: 'Deployment' with: 'Buoy-Localization-Extensions';
package: 'Buoy-Localization-Tests'
with: [ spec requires: #( 'Buoy-Localization-Extensions' 'Dependent-SUnit-Extensions' ) ];
group: 'Tests' with: 'Buoy-Localization-Tests'
]

{ #category : 'baselines' }
BaselineOfBuoy >> baselineMath: spec [

Expand Down
43 changes: 43 additions & 0 deletions source/Buoy-Localization-Extensions/String.extension.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Extension { #name : 'String' }

{ #category : '*Buoy-Localization-Extensions' }
String >> asLanguageRange [

^ LanguageRange fromString: self
]

{ #category : '*Buoy-Localization-Extensions' }
String >> asLanguageTag [

^ LanguageTag fromString: self
]

{ #category : '*Buoy-Localization-Extensions' }
String >> escaped [

^ self species new: self size streamContents: [ :result |
| stream |
stream := self readStream.
[ stream atEnd ] whileFalse: [ StringEscapingRule escape: stream next on: result ]
]
]

{ #category : '*Buoy-Localization-Extensions' }
String >> unescaped [

^ self species new: self size streamContents: [ :result |
| stream |
stream := self readStream.
[ stream atEnd ] whileFalse: [
| currentChar |
currentChar := stream next.
currentChar == $\
ifTrue: [
stream atEnd
ifTrue: [ AssertionFailed signal: 'Missing escape sequence' ]
ifFalse: [ StringEscapingRule unescape: stream next from: stream on: result ]
]
ifFalse: [ result nextPut: currentChar ]
]
]
]
1 change: 1 addition & 0 deletions source/Buoy-Localization-Extensions/package.st
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Package { #name : 'Buoy-Localization-Extensions' }
94 changes: 94 additions & 0 deletions source/Buoy-Localization-Tests/LanguageRangeTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"
A LanguageRangeTest is a test class for testing the behavior of LanguageRange
"
Class {
#name : 'LanguageRangeTest',
#superclass : 'TestCase',
#category : 'Buoy-Localization-Tests',
#package : 'Buoy-Localization-Tests'
}

{ #category : 'tests' }
LanguageRangeTest >> testAny [

self
assert: LanguageRange any printString equals: '*';
assert: LanguageRange any equals: LanguageRange any;
assert: LanguageRange any hash equals: LanguageRange any hash;
assert: LanguageRange any subtags equals: #('*')
]

{ #category : 'tests' }
LanguageRangeTest >> testAnyMatches [

| range |

range := LanguageRange any.
self
assert: ( range matches: 'en-Latn-US' asLanguageTag );
assert: ( range matches: 'en' asLanguageTag );
assert: ( range matches: 'es-ar' asLanguageTag )
]

{ #category : 'tests' }
LanguageRangeTest >> testAsLanguageRange [

self
assert: '*' asLanguageRange equals: LanguageRange any;
assert: LanguageRange any asLanguageRange equals: LanguageRange any;
assert: 'es-AR' asLanguageRange equals: ( LanguageRange from: #( 'es' 'AR' ) ) asLanguageRange
]

{ #category : 'tests' }
LanguageRangeTest >> testComparison [

| range |

range := LanguageRange from: #('en' 'US').
self
assert: range equals: ( LanguageRange fromString: 'en-us' );
assert: range hash equals: ( LanguageRange fromString: 'en-us' ) hash;
deny: range equals: LanguageRange any;
deny: range equals: ( LanguageRange from: #('en') )
]

{ #category : 'tests' }
LanguageRangeTest >> testCreation [

| range |

range := LanguageRange from: #('en' 'Latn' 'us').
self
assert: range subtags equals: #('en' 'Latn' 'US');
assert: range printString equals: 'en-Latn-US'
]

{ #category : 'tests' }
LanguageRangeTest >> testMatches [

| range |

range := LanguageRange from: #('en' 'US').
self
deny: ( range matches: 'en-Latn-US' asLanguageTag );
assert: ( range matches: 'en-US' asLanguageTag );
deny: ( range matches: 'en' asLanguageTag );
assert: ( range matches: 'en-us-x-x-x' asLanguageTag ).

range := LanguageRange from: #('en').
self
assert: ( range matches: 'en-Latn-US' asLanguageTag );
assert: ( range matches: 'en-US' asLanguageTag );
assert: ( range matches: 'en' asLanguageTag );
assert: ( range matches: 'en-us-x-x-x' asLanguageTag )
]

{ #category : 'tests' }
LanguageRangeTest >> testSpecificity [

self
assert: '*' asLanguageRange specificity equals: 0;
assert: 'es' asLanguageRange specificity equals: 1;
assert: 'es-AR' asLanguageRange specificity equals: 2;
assert: 'hy-Latn-IT-arevela' asLanguageRange specificity equals: 4
]
125 changes: 125 additions & 0 deletions source/Buoy-Localization-Tests/LanguageTagTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"
A LanguageTagTest is a test class for testing the behavior of LanguageTag
"
Class {
#name : 'LanguageTagTest',
#superclass : 'TestCase',
#category : 'Buoy-Localization-Tests',
#package : 'Buoy-Localization-Tests'
}

{ #category : 'tests' }
LanguageTagTest >> testCantCreateWhenLanguageCodeIsInvalid [

self
should: [ LanguageTag fromString: 'e' ]
raise: InstanceCreationFailed
withMessageText: 'ISO 639 language codes must be 2 or 3 letters.';
should: [ LanguageTag fromString: 'e2' ]
raise: InstanceCreationFailed
withMessageText: 'ISO 639 language codes must consist only of letters.'
]

{ #category : 'tests' }
LanguageTagTest >> testCantCreateWhenRegionIsInvalid [

self
should: [ LanguageTag fromString: 'en-A3' ]
raise: InstanceCreationFailed
withMessageText: 'Supported ISO 3166-1 codes must be 2 letters.'
]

{ #category : 'tests' }
LanguageTagTest >> testCantCreateWhenScriptIsInvalid [

self
should: [ LanguageTag fromString: 'en-L123' ]
raise: InstanceCreationFailed
withMessageText: 'ISO 15924 script codes must be 4 letters.'
]

{ #category : 'tests' }
LanguageTagTest >> testCreation [

| tag |

tag := LanguageTag fromString: 'en-Latn-US'.
self assert: tag languageCode equals: 'en'.
tag
withScriptDo: [ :script | self assert: script equals: 'Latn' ];
withRegionDo: [ :region | self assert: region equals: 'US' ].

self assert: tag subtags equals: #('en' 'Latn' 'US')
]

{ #category : 'tests' }
LanguageTagTest >> testCreationWithExtensions [

| tag |

tag := LanguageTag fromString: 'en-Latn-GB-boont-r-extended-sequence-x-private'.
self assert: tag languageCode equals: 'en'.
tag
withScriptDo: [ :script | self assert: script equals: 'Latn' ];
withRegionDo: [ :region | self assert: region equals: 'GB' ].

self
assert: tag subtags
equals: #('en' 'Latn' 'GB' 'boont' 'r' 'extended' 'sequence' 'x' 'private')
]

{ #category : 'tests' }
LanguageTagTest >> testCreationWithHorribleCase [

| tag |

tag := LanguageTag fromString: 'Es-lAtN-aR'.
self assert: tag languageCode equals: 'es'.
tag
withScriptDo: [ :script | self assert: script equals: 'Latn' ];
withRegionDo: [ :region | self assert: region equals: 'AR' ].

self assert: tag subtags equals: #('es' 'Latn' 'AR')
]

{ #category : 'tests' }
LanguageTagTest >> testCreationWithOnlyLanguage [

| tag |

tag := LanguageTag fromString: 'en'.
self assert: tag languageCode equals: 'en'.
tag
withScriptDo: [ :script | self fail ];
withRegionDo: [ :region | self fail ].

self assert: tag subtags equals: #('en')
]

{ #category : 'tests' }
LanguageTagTest >> testCreationWithRegionButNotScript [

| tag |

tag := LanguageTag fromString: 'en-us'.
self assert: tag languageCode equals: 'en'.
tag
withScriptDo: [ :script | self fail ];
withRegionDo: [ :region | self assert: region equals: 'US' ].

self assert: tag subtags equals: #('en' 'US')
]

{ #category : 'tests' }
LanguageTagTest >> testCreationWithScriptButNoRegion [

| tag |

tag := LanguageTag fromString: 'es-latn'.
self assert: tag languageCode equals: 'es'.
tag
withScriptDo: [ :script | self assert: script equals: 'Latn' ];
withRegionDo: [ :region | self fail ].

self assert: tag subtags equals: #('es' 'Latn')
]
23 changes: 23 additions & 0 deletions source/Buoy-Localization-Tests/StringEscapingRuleTest.class.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"
A StringEscapingRuleTest is a test class for testing the behavior of StringEscapingRule
"
Class {
#name : 'StringEscapingRuleTest',
#superclass : 'TestCase',
#category : 'Buoy-Localization-Tests',
#package : 'Buoy-Localization-Tests'
}

{ #category : 'tests' }
StringEscapingRuleTest >> testIsAbstract [

self assert: StringEscapingRule isAbstract
]

{ #category : 'tests' }
StringEscapingRuleTest >> testPriority [

self
assert: ReverseSolidusEscapingRule new priority > ControlCharactersEscapingRule new priority;
assert: ControlCharactersEscapingRule new priority > UnicodeCharacterEscapingRule new priority
]
Loading

0 comments on commit 74f5f3e

Please sign in to comment.