@tldraw/tlschema 4.1.0-canary.5b2a01989756 → 4.1.0-canary.62b1976714aa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-cjs/TLStore.js +3 -10
- package/dist-cjs/TLStore.js.map +2 -2
- package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
- package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
- package/dist-cjs/assets/TLImageAsset.js.map +2 -2
- package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
- package/dist-cjs/bindings/TLArrowBinding.js.map +2 -2
- package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
- package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
- package/dist-cjs/createTLSchema.js.map +2 -2
- package/dist-cjs/index.d.ts +4416 -223
- package/dist-cjs/index.js +1 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/misc/TLColor.js.map +2 -2
- package/dist-cjs/misc/TLCursor.js.map +2 -2
- package/dist-cjs/misc/TLHandle.js.map +2 -2
- package/dist-cjs/misc/TLOpacity.js.map +2 -2
- package/dist-cjs/misc/TLRichText.js.map +2 -2
- package/dist-cjs/misc/TLScribble.js.map +2 -2
- package/dist-cjs/misc/geometry-types.js.map +2 -2
- package/dist-cjs/misc/id-validator.js.map +2 -2
- package/dist-cjs/records/TLAsset.js.map +2 -2
- package/dist-cjs/records/TLBinding.js.map +2 -2
- package/dist-cjs/records/TLCamera.js.map +2 -2
- package/dist-cjs/records/TLDocument.js.map +2 -2
- package/dist-cjs/records/TLInstance.js.map +2 -2
- package/dist-cjs/records/TLPage.js.map +2 -2
- package/dist-cjs/records/TLPageState.js.map +2 -2
- package/dist-cjs/records/TLPointer.js.map +2 -2
- package/dist-cjs/records/TLPresence.js.map +2 -2
- package/dist-cjs/records/TLRecord.js.map +1 -1
- package/dist-cjs/records/TLShape.js.map +2 -2
- package/dist-cjs/recordsWithProps.js.map +2 -2
- package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
- package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
- package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
- package/dist-cjs/shapes/TLBookmarkShape.js.map +2 -2
- package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
- package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
- package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
- package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
- package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
- package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
- package/dist-cjs/shapes/TLImageShape.js.map +2 -2
- package/dist-cjs/shapes/TLLineShape.js.map +2 -2
- package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
- package/dist-cjs/shapes/TLTextShape.js.map +2 -2
- package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
- package/dist-cjs/store-migrations.js.map +2 -2
- package/dist-cjs/styles/TLColorStyle.js.map +2 -2
- package/dist-cjs/styles/TLDashStyle.js.map +2 -2
- package/dist-cjs/styles/TLFillStyle.js.map +2 -2
- package/dist-cjs/styles/TLFontStyle.js.map +2 -2
- package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
- package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
- package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
- package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
- package/dist-cjs/translations/translations.js +1 -1
- package/dist-cjs/translations/translations.js.map +2 -2
- package/dist-cjs/util-types.js.map +1 -1
- package/dist-esm/TLStore.mjs +3 -10
- package/dist-esm/TLStore.mjs.map +2 -2
- package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
- package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
- package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
- package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
- package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
- package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
- package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
- package/dist-esm/createTLSchema.mjs.map +2 -2
- package/dist-esm/index.d.mts +4416 -223
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/misc/TLColor.mjs.map +2 -2
- package/dist-esm/misc/TLCursor.mjs.map +2 -2
- package/dist-esm/misc/TLHandle.mjs.map +2 -2
- package/dist-esm/misc/TLOpacity.mjs.map +2 -2
- package/dist-esm/misc/TLRichText.mjs.map +2 -2
- package/dist-esm/misc/TLScribble.mjs.map +2 -2
- package/dist-esm/misc/geometry-types.mjs.map +2 -2
- package/dist-esm/misc/id-validator.mjs.map +2 -2
- package/dist-esm/records/TLAsset.mjs.map +2 -2
- package/dist-esm/records/TLBinding.mjs.map +2 -2
- package/dist-esm/records/TLCamera.mjs.map +2 -2
- package/dist-esm/records/TLDocument.mjs.map +2 -2
- package/dist-esm/records/TLInstance.mjs.map +2 -2
- package/dist-esm/records/TLPage.mjs.map +2 -2
- package/dist-esm/records/TLPageState.mjs.map +2 -2
- package/dist-esm/records/TLPointer.mjs.map +2 -2
- package/dist-esm/records/TLPresence.mjs.map +2 -2
- package/dist-esm/records/TLShape.mjs.map +2 -2
- package/dist-esm/recordsWithProps.mjs.map +2 -2
- package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
- package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
- package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
- package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
- package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
- package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
- package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
- package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
- package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
- package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
- package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
- package/dist-esm/store-migrations.mjs.map +2 -2
- package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
- package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
- package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
- package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
- package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
- package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
- package/dist-esm/translations/translations.mjs +1 -1
- package/dist-esm/translations/translations.mjs.map +2 -2
- package/package.json +5 -5
- package/src/TLStore.test.ts +644 -0
- package/src/TLStore.ts +205 -20
- package/src/assets/TLBaseAsset.ts +90 -7
- package/src/assets/TLBookmarkAsset.test.ts +96 -0
- package/src/assets/TLBookmarkAsset.ts +52 -2
- package/src/assets/TLImageAsset.test.ts +213 -0
- package/src/assets/TLImageAsset.ts +60 -2
- package/src/assets/TLVideoAsset.test.ts +105 -0
- package/src/assets/TLVideoAsset.ts +93 -4
- package/src/bindings/TLArrowBinding.test.ts +55 -0
- package/src/bindings/TLArrowBinding.ts +132 -10
- package/src/bindings/TLBaseBinding.ts +140 -3
- package/src/createPresenceStateDerivation.test.ts +158 -0
- package/src/createPresenceStateDerivation.ts +71 -2
- package/src/createTLSchema.test.ts +181 -0
- package/src/createTLSchema.ts +164 -7
- package/src/index.ts +32 -0
- package/src/misc/TLColor.ts +50 -6
- package/src/misc/TLCursor.ts +110 -8
- package/src/misc/TLHandle.ts +86 -6
- package/src/misc/TLOpacity.ts +51 -2
- package/src/misc/TLRichText.ts +56 -3
- package/src/misc/TLScribble.ts +105 -5
- package/src/misc/geometry-types.ts +30 -2
- package/src/misc/id-validator.test.ts +50 -0
- package/src/misc/id-validator.ts +20 -1
- package/src/records/TLAsset.test.ts +234 -0
- package/src/records/TLAsset.ts +165 -8
- package/src/records/TLBinding.test.ts +22 -0
- package/src/records/TLBinding.ts +277 -11
- package/src/records/TLCamera.test.ts +19 -0
- package/src/records/TLCamera.ts +118 -7
- package/src/records/TLDocument.test.ts +35 -0
- package/src/records/TLDocument.ts +148 -8
- package/src/records/TLInstance.test.ts +201 -0
- package/src/records/TLInstance.ts +117 -9
- package/src/records/TLPage.test.ts +110 -0
- package/src/records/TLPage.ts +106 -8
- package/src/records/TLPageState.test.ts +228 -0
- package/src/records/TLPageState.ts +88 -7
- package/src/records/TLPointer.test.ts +63 -0
- package/src/records/TLPointer.ts +105 -7
- package/src/records/TLPresence.test.ts +190 -0
- package/src/records/TLPresence.ts +99 -5
- package/src/records/TLRecord.test.ts +70 -0
- package/src/records/TLRecord.ts +43 -1
- package/src/records/TLShape.test.ts +232 -0
- package/src/records/TLShape.ts +289 -12
- package/src/recordsWithProps.test.ts +188 -0
- package/src/recordsWithProps.ts +131 -2
- package/src/shapes/ShapeWithCrop.test.ts +18 -0
- package/src/shapes/ShapeWithCrop.ts +64 -2
- package/src/shapes/TLArrowShape.test.ts +505 -0
- package/src/shapes/TLArrowShape.ts +188 -10
- package/src/shapes/TLBaseShape.test.ts +142 -0
- package/src/shapes/TLBaseShape.ts +103 -4
- package/src/shapes/TLBookmarkShape.test.ts +122 -0
- package/src/shapes/TLBookmarkShape.ts +58 -4
- package/src/shapes/TLDrawShape.test.ts +177 -0
- package/src/shapes/TLDrawShape.ts +97 -6
- package/src/shapes/TLEmbedShape.test.ts +286 -0
- package/src/shapes/TLEmbedShape.ts +57 -4
- package/src/shapes/TLFrameShape.test.ts +71 -0
- package/src/shapes/TLFrameShape.ts +59 -4
- package/src/shapes/TLGeoShape.test.ts +247 -0
- package/src/shapes/TLGeoShape.ts +103 -7
- package/src/shapes/TLGroupShape.test.ts +59 -0
- package/src/shapes/TLGroupShape.ts +52 -4
- package/src/shapes/TLHighlightShape.test.ts +325 -0
- package/src/shapes/TLHighlightShape.ts +79 -4
- package/src/shapes/TLImageShape.test.ts +534 -0
- package/src/shapes/TLImageShape.ts +105 -5
- package/src/shapes/TLLineShape.test.ts +269 -0
- package/src/shapes/TLLineShape.ts +128 -8
- package/src/shapes/TLNoteShape.test.ts +1568 -0
- package/src/shapes/TLNoteShape.ts +97 -4
- package/src/shapes/TLTextShape.test.ts +407 -0
- package/src/shapes/TLTextShape.ts +94 -4
- package/src/shapes/TLVideoShape.test.ts +112 -0
- package/src/shapes/TLVideoShape.ts +99 -4
- package/src/store-migrations.test.ts +88 -0
- package/src/store-migrations.ts +47 -1
- package/src/styles/TLColorStyle.test.ts +439 -0
- package/src/styles/TLColorStyle.ts +228 -10
- package/src/styles/TLDashStyle.ts +54 -2
- package/src/styles/TLFillStyle.ts +54 -2
- package/src/styles/TLFontStyle.ts +72 -3
- package/src/styles/TLHorizontalAlignStyle.ts +55 -2
- package/src/styles/TLSizeStyle.ts +54 -2
- package/src/styles/TLTextAlignStyle.ts +52 -2
- package/src/styles/TLVerticalAlignStyle.ts +52 -2
- package/src/translations/translations.test.ts +378 -35
- package/src/translations/translations.ts +157 -10
- package/src/util-types.ts +51 -1
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
import { T } from '@tldraw/validate'
|
|
2
2
|
import { StyleProp } from './StyleProp'
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Default text alignment style property used by tldraw text shapes.
|
|
6
|
+
* Controls how text content is aligned within text-based shapes like text boxes and notes.
|
|
7
|
+
*
|
|
8
|
+
* Available values:
|
|
9
|
+
* - `start` - Align text to the start (left in LTR, right in RTL)
|
|
10
|
+
* - `middle` - Center text horizontally
|
|
11
|
+
* - `end` - Align text to the end (right in LTR, left in RTL)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { DefaultTextAlignStyle } from '@tldraw/tlschema'
|
|
16
|
+
*
|
|
17
|
+
* // Use in text shape props definition
|
|
18
|
+
* interface MyTextShapeProps {
|
|
19
|
+
* textAlign: typeof DefaultTextAlignStyle
|
|
20
|
+
* // other props...
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Create a text shape with center alignment
|
|
24
|
+
* const textShape = {
|
|
25
|
+
* // ... other properties
|
|
26
|
+
* props: {
|
|
27
|
+
* textAlign: 'middle' as const,
|
|
28
|
+
* // ... other props
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
5
35
|
export const DefaultTextAlignStyle = StyleProp.defineEnum('tldraw:textAlign', {
|
|
6
36
|
defaultValue: 'start',
|
|
7
37
|
values: ['start', 'middle', 'end'],
|
|
8
38
|
})
|
|
9
39
|
|
|
10
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Type representing a default text alignment style value.
|
|
42
|
+
* This is a union type of all available text alignment options.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { TLDefaultTextAlignStyle } from '@tldraw/tlschema'
|
|
47
|
+
*
|
|
48
|
+
* // Valid text alignment values
|
|
49
|
+
* const leftAlign: TLDefaultTextAlignStyle = 'start'
|
|
50
|
+
* const centerAlign: TLDefaultTextAlignStyle = 'middle'
|
|
51
|
+
* const rightAlign: TLDefaultTextAlignStyle = 'end'
|
|
52
|
+
*
|
|
53
|
+
* // Use in a function parameter
|
|
54
|
+
* function setTextAlignment(align: TLDefaultTextAlignStyle) {
|
|
55
|
+
* // Apply text alignment to text shape
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
11
61
|
export type TLDefaultTextAlignStyle = T.TypeOf<typeof DefaultTextAlignStyle>
|
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
import { T } from '@tldraw/validate'
|
|
2
2
|
import { StyleProp } from './StyleProp'
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Default vertical alignment style property used by tldraw shapes for text positioning.
|
|
6
|
+
* Controls how text content is vertically aligned within shape boundaries.
|
|
7
|
+
*
|
|
8
|
+
* Available values:
|
|
9
|
+
* - `start` - Align text to the top
|
|
10
|
+
* - `middle` - Center text vertically (default)
|
|
11
|
+
* - `end` - Align text to the bottom
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { DefaultVerticalAlignStyle } from '@tldraw/tlschema'
|
|
16
|
+
*
|
|
17
|
+
* // Use in shape props definition
|
|
18
|
+
* interface MyShapeProps {
|
|
19
|
+
* verticalAlign: typeof DefaultVerticalAlignStyle
|
|
20
|
+
* // other props...
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* // Create a shape with top-aligned text
|
|
24
|
+
* const shape = {
|
|
25
|
+
* // ... other properties
|
|
26
|
+
* props: {
|
|
27
|
+
* verticalAlign: 'start' as const,
|
|
28
|
+
* // ... other props
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
5
35
|
export const DefaultVerticalAlignStyle = StyleProp.defineEnum('tldraw:verticalAlign', {
|
|
6
36
|
defaultValue: 'middle',
|
|
7
37
|
values: ['start', 'middle', 'end'],
|
|
8
38
|
})
|
|
9
39
|
|
|
10
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Type representing a default vertical alignment style value.
|
|
42
|
+
* This is a union type of all available vertical alignment options.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* import { TLDefaultVerticalAlignStyle } from '@tldraw/tlschema'
|
|
47
|
+
*
|
|
48
|
+
* // Valid vertical alignment values
|
|
49
|
+
* const topAlign: TLDefaultVerticalAlignStyle = 'start'
|
|
50
|
+
* const centerAlign: TLDefaultVerticalAlignStyle = 'middle'
|
|
51
|
+
* const bottomAlign: TLDefaultVerticalAlignStyle = 'end'
|
|
52
|
+
*
|
|
53
|
+
* // Use in a function parameter
|
|
54
|
+
* function setVerticalAlignment(align: TLDefaultVerticalAlignStyle) {
|
|
55
|
+
* // Apply vertical alignment to text
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
11
61
|
export type TLDefaultVerticalAlignStyle = T.TypeOf<typeof DefaultVerticalAlignStyle>
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
LANGUAGES,
|
|
4
|
+
TLLanguage,
|
|
5
|
+
_getDefaultTranslationLocale,
|
|
6
|
+
getDefaultTranslationLocale,
|
|
7
|
+
} from './translations'
|
|
2
8
|
|
|
3
9
|
interface DefaultLanguageTest {
|
|
4
10
|
name: string
|
|
@@ -6,38 +12,375 @@ interface DefaultLanguageTest {
|
|
|
6
12
|
output: string
|
|
7
13
|
}
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
15
|
+
const originalWindow = global.window
|
|
16
|
+
|
|
17
|
+
describe('translations.ts', () => {
|
|
18
|
+
describe('exports', () => {
|
|
19
|
+
it('should export LANGUAGES constant', () => {
|
|
20
|
+
expect(LANGUAGES).toBeDefined()
|
|
21
|
+
expect(Array.isArray(LANGUAGES)).toBe(true)
|
|
22
|
+
expect(LANGUAGES.length).toBeGreaterThan(0)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should export TLLanguage type', () => {
|
|
26
|
+
// Test that TLLanguage type works correctly
|
|
27
|
+
const testLanguage: TLLanguage = LANGUAGES[0]
|
|
28
|
+
expect(testLanguage).toHaveProperty('locale')
|
|
29
|
+
expect(testLanguage).toHaveProperty('label')
|
|
30
|
+
expect(typeof testLanguage.locale).toBe('string')
|
|
31
|
+
expect(typeof testLanguage.label).toBe('string')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should export getDefaultTranslationLocale function', () => {
|
|
35
|
+
expect(typeof getDefaultTranslationLocale).toBe('function')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should export _getDefaultTranslationLocale function', () => {
|
|
39
|
+
expect(typeof _getDefaultTranslationLocale).toBe('function')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('getDefaultTranslationLocale', () => {
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
// Restore original window
|
|
46
|
+
global.window = originalWindow
|
|
47
|
+
vi.clearAllMocks()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should return default locale in browser environment', () => {
|
|
51
|
+
// Mock browser environment
|
|
52
|
+
global.window = {
|
|
53
|
+
navigator: {
|
|
54
|
+
languages: ['fr', 'en'],
|
|
55
|
+
},
|
|
56
|
+
} as any
|
|
57
|
+
|
|
58
|
+
const locale = getDefaultTranslationLocale()
|
|
59
|
+
expect(locale).toBe('fr')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should use navigator.languages when available', () => {
|
|
63
|
+
// Mock browser environment with navigator.languages
|
|
64
|
+
global.window = {
|
|
65
|
+
navigator: {
|
|
66
|
+
languages: ['de', 'es', 'en'],
|
|
67
|
+
},
|
|
68
|
+
} as any
|
|
69
|
+
|
|
70
|
+
const locale = getDefaultTranslationLocale()
|
|
71
|
+
expect(locale).toBe('de')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should fallback to English when navigator.languages is null/undefined', () => {
|
|
75
|
+
// Mock browser environment with null languages
|
|
76
|
+
global.window = {
|
|
77
|
+
navigator: {
|
|
78
|
+
languages: null,
|
|
79
|
+
},
|
|
80
|
+
} as any
|
|
81
|
+
|
|
82
|
+
const locale = getDefaultTranslationLocale()
|
|
83
|
+
expect(locale).toBe('en')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should return English in server environment (no window)', () => {
|
|
87
|
+
// Mock server environment
|
|
88
|
+
delete (global as any).window
|
|
89
|
+
|
|
90
|
+
const locale = getDefaultTranslationLocale()
|
|
91
|
+
expect(locale).toBe('en')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should handle undefined navigator gracefully', () => {
|
|
95
|
+
// Mock browser environment without navigator
|
|
96
|
+
global.window = {
|
|
97
|
+
navigator: undefined,
|
|
98
|
+
} as any
|
|
99
|
+
|
|
100
|
+
const locale = getDefaultTranslationLocale()
|
|
101
|
+
expect(locale).toBe('en')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should handle empty languages array', () => {
|
|
105
|
+
// Mock browser environment with empty languages array
|
|
106
|
+
global.window = {
|
|
107
|
+
navigator: {
|
|
108
|
+
languages: [],
|
|
109
|
+
},
|
|
110
|
+
} as any
|
|
111
|
+
|
|
112
|
+
const locale = getDefaultTranslationLocale()
|
|
113
|
+
expect(locale).toBe('en')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('should work with complex browser language preferences', () => {
|
|
117
|
+
// Test realistic browser scenario
|
|
118
|
+
global.window = {
|
|
119
|
+
navigator: {
|
|
120
|
+
languages: ['zh-TW', 'zh', 'en-US', 'en'],
|
|
121
|
+
},
|
|
122
|
+
} as any
|
|
123
|
+
|
|
124
|
+
const locale = getDefaultTranslationLocale()
|
|
125
|
+
expect(locale).toBe('zh-tw') // Should match zh-TW exactly
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('_getDefaultTranslationLocale (internal logic)', () => {
|
|
130
|
+
const tests: DefaultLanguageTest[] = [
|
|
131
|
+
{
|
|
132
|
+
name: 'finds a matching language locale',
|
|
133
|
+
input: ['fr'],
|
|
134
|
+
output: 'fr',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'finds a matching region locale',
|
|
138
|
+
input: ['pt-PT'],
|
|
139
|
+
output: 'pt-pt',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'picks a region locale if no language locale available',
|
|
143
|
+
input: ['pt'],
|
|
144
|
+
output: 'pt-br',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'picks a language locale if no region locale available',
|
|
148
|
+
input: ['fr-CA'],
|
|
149
|
+
output: 'fr',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'picks the first language that loosely matches',
|
|
153
|
+
input: ['fr-CA', 'pt-PT'],
|
|
154
|
+
output: 'fr',
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
for (const test of tests) {
|
|
159
|
+
it(test.name, () => {
|
|
160
|
+
expect(_getDefaultTranslationLocale(test.input)).toEqual(test.output)
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
it('should return "en" when no locales provided', () => {
|
|
165
|
+
expect(_getDefaultTranslationLocale([])).toBe('en')
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should return "en" when no supported locales found', () => {
|
|
169
|
+
expect(_getDefaultTranslationLocale(['xyz', 'abc', 'unknown'])).toBe('en')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('should handle case insensitive matching', () => {
|
|
173
|
+
expect(_getDefaultTranslationLocale(['FR'])).toBe('fr')
|
|
174
|
+
expect(_getDefaultTranslationLocale(['PT-BR'])).toBe('pt-br')
|
|
175
|
+
expect(_getDefaultTranslationLocale(['ZH-CN'])).toBe('zh-cn')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('should handle mixed case and separators', () => {
|
|
179
|
+
expect(_getDefaultTranslationLocale(['pt_BR'])).toBe('pt-br')
|
|
180
|
+
expect(_getDefaultTranslationLocale(['zh_CN'])).toBe('zh-cn')
|
|
181
|
+
expect(_getDefaultTranslationLocale(['KO_KR'])).toBe('ko-kr')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should prioritize exact matches over fallbacks', () => {
|
|
185
|
+
expect(_getDefaultTranslationLocale(['pt-pt', 'pt'])).toBe('pt-pt')
|
|
186
|
+
expect(_getDefaultTranslationLocale(['zh-tw', 'zh'])).toBe('zh-tw')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('should handle default region assignments correctly', () => {
|
|
190
|
+
// Chinese defaults to zh-cn
|
|
191
|
+
expect(_getDefaultTranslationLocale(['zh'])).toBe('zh-cn')
|
|
192
|
+
// Portuguese defaults to pt-br
|
|
193
|
+
expect(_getDefaultTranslationLocale(['pt'])).toBe('pt-br')
|
|
194
|
+
// Korean defaults to ko-kr
|
|
195
|
+
expect(_getDefaultTranslationLocale(['ko'])).toBe('ko-kr')
|
|
196
|
+
// Hindi defaults to hi-in
|
|
197
|
+
expect(_getDefaultTranslationLocale(['hi'])).toBe('hi-in')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('should handle region-to-language fallback', () => {
|
|
201
|
+
// fr-CA should fallback to fr (if we only have generic French)
|
|
202
|
+
expect(_getDefaultTranslationLocale(['fr-CA'])).toBe('fr')
|
|
203
|
+
// es-MX should fallback to es
|
|
204
|
+
expect(_getDefaultTranslationLocale(['es-MX'])).toBe('es')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('should respect locale priority order', () => {
|
|
208
|
+
// First supported locale should win
|
|
209
|
+
expect(_getDefaultTranslationLocale(['unsupported', 'fr', 'de'])).toBe('fr')
|
|
210
|
+
expect(_getDefaultTranslationLocale(['xyz', 'de', 'fr'])).toBe('de')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should handle complex regional scenarios', () => {
|
|
214
|
+
// zh-HK not supported, should fallback to default zh-cn (doesn't continue to next locale)
|
|
215
|
+
expect(_getDefaultTranslationLocale(['zh-HK', 'zh-TW', 'en'])).toBe('zh-cn')
|
|
216
|
+
// zh-SG not supported, should fallback to default zh-cn
|
|
217
|
+
expect(_getDefaultTranslationLocale(['zh-SG', 'zh-CN', 'en'])).toBe('zh-cn')
|
|
218
|
+
// Test exact match takes priority
|
|
219
|
+
expect(_getDefaultTranslationLocale(['zh-TW', 'zh-CN'])).toBe('zh-tw')
|
|
220
|
+
// Test fallback behavior
|
|
221
|
+
expect(_getDefaultTranslationLocale(['zh-HK', 'zh'])).toBe('zh-cn')
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('should work with single locale array', () => {
|
|
225
|
+
expect(_getDefaultTranslationLocale(['fr'])).toBe('fr')
|
|
226
|
+
expect(_getDefaultTranslationLocale(['unsupported'])).toBe('en')
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('should handle whitespace and malformed locales gracefully', () => {
|
|
230
|
+
// These should not crash but return fallback
|
|
231
|
+
expect(_getDefaultTranslationLocale([' '])).toBe('en')
|
|
232
|
+
expect(_getDefaultTranslationLocale([''])).toBe('en')
|
|
233
|
+
// Malformed locales should fallback to English when they can't be parsed properly
|
|
234
|
+
expect(_getDefaultTranslationLocale(['fr-'])).toBe('en')
|
|
235
|
+
// But valid partial matches should work
|
|
236
|
+
expect(_getDefaultTranslationLocale(['fr', 'en'])).toBe('fr')
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
describe('getSupportedLocale (internal function behavior)', () => {
|
|
241
|
+
// Note: getSupportedLocale is not exported, but we can test its behavior through _getDefaultTranslationLocale
|
|
242
|
+
it('should find exact locale matches', () => {
|
|
243
|
+
// These test the internal getSupportedLocale logic through the public API
|
|
244
|
+
expect(_getDefaultTranslationLocale(['en'])).toBe('en')
|
|
245
|
+
expect(_getDefaultTranslationLocale(['fr'])).toBe('fr')
|
|
246
|
+
expect(_getDefaultTranslationLocale(['pt-br'])).toBe('pt-br')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('should handle case normalization in exact matches', () => {
|
|
250
|
+
expect(_getDefaultTranslationLocale(['EN'])).toBe('en')
|
|
251
|
+
expect(_getDefaultTranslationLocale(['PT-BR'])).toBe('pt-br')
|
|
252
|
+
expect(_getDefaultTranslationLocale(['ZH-TW'])).toBe('zh-tw')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it('should fall back from region to language', () => {
|
|
256
|
+
// fr-CA -> fr (assuming we have generic French)
|
|
257
|
+
expect(_getDefaultTranslationLocale(['fr-CA'])).toBe('fr')
|
|
258
|
+
// es-AR -> es
|
|
259
|
+
expect(_getDefaultTranslationLocale(['es-AR'])).toBe('es')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should assign default regions for base languages', () => {
|
|
263
|
+
// zh -> zh-cn (default Chinese region)
|
|
264
|
+
expect(_getDefaultTranslationLocale(['zh'])).toBe('zh-cn')
|
|
265
|
+
// pt -> pt-br (default Portuguese region)
|
|
266
|
+
expect(_getDefaultTranslationLocale(['pt'])).toBe('pt-br')
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('should return null for unsupported languages (via fallback to en)', () => {
|
|
270
|
+
// Unsupported languages should result in English fallback
|
|
271
|
+
expect(_getDefaultTranslationLocale(['xyz'])).toBe('en')
|
|
272
|
+
expect(_getDefaultTranslationLocale(['unsupported-locale'])).toBe('en')
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
describe('DEFAULT_LOCALE_REGIONS behavior', () => {
|
|
277
|
+
it('should apply Chinese default region correctly', () => {
|
|
278
|
+
expect(_getDefaultTranslationLocale(['zh'])).toBe('zh-cn')
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it('should apply Portuguese default region correctly', () => {
|
|
282
|
+
expect(_getDefaultTranslationLocale(['pt'])).toBe('pt-br')
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('should apply Korean default region correctly', () => {
|
|
286
|
+
expect(_getDefaultTranslationLocale(['ko'])).toBe('ko-kr')
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('should apply Hindi default region correctly', () => {
|
|
290
|
+
expect(_getDefaultTranslationLocale(['hi'])).toBe('hi-in')
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('should not affect languages without default regions', () => {
|
|
294
|
+
// Languages like French, Spanish, German don't have default regions
|
|
295
|
+
expect(_getDefaultTranslationLocale(['fr'])).toBe('fr')
|
|
296
|
+
expect(_getDefaultTranslationLocale(['es'])).toBe('es')
|
|
297
|
+
expect(_getDefaultTranslationLocale(['de'])).toBe('de')
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
describe('TLLanguage type integration', () => {
|
|
302
|
+
it('should work correctly with TypeScript type system', () => {
|
|
303
|
+
// Test that return values match the TLLanguage locale type
|
|
304
|
+
const result: TLLanguage['locale'] = _getDefaultTranslationLocale(['fr'])
|
|
305
|
+
expect(result).toBe('fr')
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('should be compatible with LANGUAGES array', () => {
|
|
309
|
+
const allLocales: TLLanguage['locale'][] = LANGUAGES.map((lang) => lang.locale)
|
|
310
|
+
|
|
311
|
+
// Every result should be a valid locale from LANGUAGES
|
|
312
|
+
const testLocales = ['fr', 'pt-br', 'zh-cn', 'unsupported']
|
|
313
|
+
for (const locale of testLocales) {
|
|
314
|
+
const result = _getDefaultTranslationLocale([locale])
|
|
315
|
+
expect(allLocales).toContain(result)
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
describe('edge cases and robustness', () => {
|
|
321
|
+
it('should handle null and undefined inputs gracefully', () => {
|
|
322
|
+
// These should not crash the function
|
|
323
|
+
expect(() => _getDefaultTranslationLocale([] as any)).not.toThrow()
|
|
324
|
+
expect(_getDefaultTranslationLocale([])).toBe('en')
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it('should handle very large locale arrays', () => {
|
|
328
|
+
const manyLocales = Array.from({ length: 1000 }, (_, i) => `lang-${i}`).concat(['fr']) // Add a valid one at the end
|
|
329
|
+
|
|
330
|
+
expect(_getDefaultTranslationLocale(manyLocales)).toBe('fr')
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('should handle special characters in locale strings', () => {
|
|
334
|
+
// Should not crash on malformed input
|
|
335
|
+
expect(() => _getDefaultTranslationLocale(['@#$%', 'fr'])).not.toThrow()
|
|
336
|
+
expect(_getDefaultTranslationLocale(['@#$%', 'fr'])).toBe('fr')
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it('should handle extremely long locale strings', () => {
|
|
340
|
+
const longLocale = 'a'.repeat(1000)
|
|
341
|
+
expect(() => _getDefaultTranslationLocale([longLocale, 'fr'])).not.toThrow()
|
|
342
|
+
expect(_getDefaultTranslationLocale([longLocale, 'fr'])).toBe('fr')
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
it('should be consistent across multiple calls', () => {
|
|
346
|
+
const input = ['de', 'fr', 'es']
|
|
347
|
+
const result1 = _getDefaultTranslationLocale(input)
|
|
348
|
+
const result2 = _getDefaultTranslationLocale(input)
|
|
349
|
+
const result3 = _getDefaultTranslationLocale([...input]) // Spread to ensure different array
|
|
350
|
+
|
|
351
|
+
expect(result1).toBe(result2)
|
|
352
|
+
expect(result2).toBe(result3)
|
|
353
|
+
expect(result1).toBe('de')
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
describe('real-world usage scenarios', () => {
|
|
358
|
+
it('should handle typical browser language lists', () => {
|
|
359
|
+
// Common browser scenarios
|
|
360
|
+
expect(_getDefaultTranslationLocale(['en-US', 'en'])).toBe('en')
|
|
361
|
+
expect(_getDefaultTranslationLocale(['es-ES', 'es', 'en'])).toBe('es')
|
|
362
|
+
expect(_getDefaultTranslationLocale(['fr-FR', 'fr', 'en-US', 'en'])).toBe('fr')
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
it('should work for mobile browser scenarios', () => {
|
|
366
|
+
// iOS Safari typical format
|
|
367
|
+
expect(_getDefaultTranslationLocale(['zh-Hans-CN', 'zh-Hans', 'zh'])).toBe('zh-cn')
|
|
368
|
+
// Android Chrome typical format
|
|
369
|
+
expect(_getDefaultTranslationLocale(['pt-BR', 'pt', 'en-US'])).toBe('pt-br')
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
it('should handle multilingual user preferences', () => {
|
|
373
|
+
// User who speaks multiple languages
|
|
374
|
+
expect(_getDefaultTranslationLocale(['de-CH', 'fr-CH', 'it-CH', 'en'])).toBe('de')
|
|
375
|
+
expect(_getDefaultTranslationLocale(['es-MX', 'en-US', 'fr'])).toBe('es')
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
it('should work in internationalization scenarios', () => {
|
|
379
|
+
// Various international format inputs
|
|
380
|
+
expect(_getDefaultTranslationLocale(['ja-JP'])).toBe('ja')
|
|
381
|
+
expect(_getDefaultTranslationLocale(['ko-KR'])).toBe('ko-kr')
|
|
382
|
+
expect(_getDefaultTranslationLocale(['ar-SA'])).toBe('ar')
|
|
383
|
+
expect(_getDefaultTranslationLocale(['hi-IN'])).toBe('hi-in')
|
|
384
|
+
})
|
|
385
|
+
})
|
|
43
386
|
})
|