forked from lokalise/i18n-ally
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnext-intl.ts
115 lines (94 loc) Β· 3.1 KB
/
next-intl.ts
1
2
3
4
5
6
7
8
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { TextDocument } from 'vscode'
import { Framework, ScopeRange } from './base'
import { LanguageId } from '~/utils'
import { RewriteKeySource, RewriteKeyContext, KeyStyle } from '~/core'
class NextIntlFramework extends Framework {
id = 'next-intl'
display = 'next-intl'
namespaceDelimiter = '.'
perferredKeystyle?: KeyStyle = 'nested'
namespaceDelimiters = ['.']
namespaceDelimitersRegex = /[\.]/g
detection = {
packageJSON: [
'next-intl',
],
}
languageIds: LanguageId[] = [
'javascript',
'typescript',
'javascriptreact',
'typescriptreact',
'ejs',
]
usageMatchRegex = [
// Basic usage
'[^\\w\\d]t\\s*\\(\\s*[\'"`]({key})[\'"`]',
// Rich text
'[^\\w\\d]t\\s*\.rich\\s*\\(\\s*[\'"`]({key})[\'"`]',
// Markup text
'[^\\w\\d]t\\s*\.markup\\s*\\(\\s*[\'"`]({key})[\'"`]',
// Raw text
'[^\\w\\d]t\\s*\.raw\\s*\\(\\s*[\'"`]({key})[\'"`]',
]
refactorTemplates(keypath: string) {
// Ideally we'd automatically consider the namespace here. Since this
// doesn't seem to be possible though, we'll generate all permutations for
// the `keypath`. E.g. `one.two.three` will generate `three`, `two.three`,
// `one.two.three`.
const keypaths = keypath.split('.').map((cur, index, parts) => {
return parts.slice(parts.length - index - 1).join('.')
})
return [
...keypaths.map(cur =>
`{t('${cur}')}`,
),
...keypaths.map(cur =>
`t('${cur}')`,
),
]
}
rewriteKeys(key: string, source: RewriteKeySource, context: RewriteKeyContext = {}) {
const dottedKey = key.split(this.namespaceDelimitersRegex).join('.')
// When the namespace is explicitly set, ignore the current namespace scope
if (
this.namespaceDelimiters.some(delimiter => key.includes(delimiter))
&& context.namespace
&& dottedKey.startsWith(context.namespace.split(this.namespaceDelimitersRegex).join('.'))
) {
// +1 for the an extra `.`
key = key.slice(context.namespace.length + 1)
}
return dottedKey
}
getScopeRange(document: TextDocument): ScopeRange[] | undefined {
if (!this.languageIds.includes(document.languageId as any))
return
const ranges: ScopeRange[] = []
const text = document.getText()
// Find matches of `useTranslations` and `getTranslations`. Later occurences will
// override previous ones (this allows for multiple components with different
// namespaces in the same file).
const regex = /(useTranslations\(\s*|getTranslations\(\s*)(['"`](.*?)['"`])?/g
let prevGlobalScope = false
for (const match of text.matchAll(regex)) {
if (typeof match.index !== 'number')
continue
const namespace = match[3]
// End previous scope
if (prevGlobalScope)
ranges[ranges.length - 1].end = match.index
// Start a new scope if a namespace is provided
if (namespace) {
prevGlobalScope = true
ranges.push({
start: match.index,
end: text.length,
namespace,
})
}
}
return ranges
}
}
export default NextIntlFramework