Skip to content

Commit ff7417c

Browse files
aranardaranard-thetradedeskterales
authored
feat: adding new scopeRangeRegex config to the custom framework (#926)
* Feat: Adding new namespaceMatchRegex config to the custom framework options * Empty commit to trigger CI --------- Co-authored-by: Amanda Ranard <[email protected]> Co-authored-by: Alex Terehov <[email protected]>
1 parent e25e1cd commit ff7417c

File tree

12 files changed

+205
-2
lines changed

12 files changed

+205
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
languageIds:
2+
- javascript
3+
- typescript
4+
- javascriptreact
5+
- typescriptreact
6+
scopeRangeRegex: "customScopeRangeFn\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
7+
monopoly: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"i18n-ally.localesPaths": "public/locales",
3+
"i18n-ally.enabledFrameworks": ["react", "i18next", "custom"],
4+
"i18n-ally.namespace": true,
5+
"i18n-ally.pathMatcher": "{locale}/{namespaces}.json",
6+
"i18n-ally.keystyle": "nested"
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "custom-scope-range",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"i18next": "20.3.0",
7+
"i18next-xhr-backend": "3.2.2",
8+
"react": "17.0.2",
9+
"react-dom": "17.0.2",
10+
"react-i18next": "11.9.0"
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6+
<meta name="theme-color" content="#000000">
7+
<title>React App</title>
8+
</head>
9+
<body>
10+
<noscript>
11+
You need to enable JavaScript to run this app.
12+
</noscript>
13+
<div id="root"></div>
14+
</body>
15+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"title": "Home"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"title": "hello",
3+
"description": {
4+
"part1": "To get started, edit <1>src/App.js</1> and save to reload.",
5+
"part2": "Switch language between english and german using buttons above."
6+
},
7+
"titlew": "ok",
8+
"foo": {
9+
"bar": "foobar"
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.App {
2+
text-align: center;
3+
}
4+
5+
.App-header {
6+
background-color: #222;
7+
height: 100px;
8+
padding: 20px;
9+
color: white;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/* eslint-disable no-undef */
2+
/* eslint-disable @typescript-eslint/no-unused-vars */
3+
/* eslint-disable no-unused-vars */
4+
// eslint-disable-next-line no-use-before-define
5+
import React from 'react'
6+
import { Trans } from 'react-i18next'
7+
import { customScopeRangeFn } from './customScopeRange'
8+
import './App.css'
9+
10+
// Component using the Trans component
11+
function MyComponent() {
12+
return (
13+
<Trans i18nKey="translation:description.part1">
14+
To get started, edit <code>src/App.js</code> and save to reload.
15+
</Trans>
16+
)
17+
}
18+
19+
// page uses the hook
20+
function Page() {
21+
const { t } = customScopeRangeFn()
22+
23+
return (
24+
<div className="App">
25+
<div className="App-intro">
26+
<MyComponent />
27+
</div>
28+
<div>{t('translation:description.part2')}</div>
29+
{/* plain <Trans>, #423 */}
30+
<Trans i18nKey="translation:description.part2">Fallback text</Trans>
31+
</div>
32+
)
33+
}
34+
35+
// function with scope
36+
function Page2() {
37+
const { t } = customScopeRangeFn(['translation:foo'])
38+
39+
// inside default namespace ("foo.bar")
40+
t('bar')
41+
42+
// explicit namespace
43+
t('pages.home:title')
44+
t('pages/home:title')
45+
}
46+
47+
// function with another scope
48+
function Page3() {
49+
const { t } = customScopeRangeFn('pages/home')
50+
51+
t('title')
52+
53+
// explicit namespace
54+
t('translation:title')
55+
}
56+
57+
// function with scope in options
58+
function Page4() {
59+
const { t } = customScopeRangeFn('pages/home')
60+
61+
t('title')
62+
63+
// explicit namespace
64+
t('title', { ns: 'translation' })
65+
}
66+
67+
// component with scope in props
68+
function Page5() {
69+
const { t } = customScopeRangeFn('pages/home')
70+
71+
return (
72+
<div className="App">
73+
<Trans t={t} i18nKey="title"></Trans>
74+
{/* explicit namespace */}
75+
<Trans t={t} i18nKey="title" ns="translation"></Trans>
76+
</div>
77+
)
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { useTranslation as customScopeRangeFn } from 'react-i18next'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2+
import React from 'react'
3+
import i18n from 'i18next'
4+
import { initReactI18next } from 'react-i18next'
5+
6+
i18n
7+
.use(Backend)
8+
.use(initReactI18next)
9+
.init({
10+
fallbackLng: 'en',
11+
debug: true,
12+
13+
interpolation: {
14+
escapeValue: false, // not needed for react as it escapes by default
15+
},
16+
})
17+
18+
export default i18n
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-use-before-define
2+
import React from 'react'
3+
import ReactDOM from 'react-dom'
4+
import App from './App'
5+
6+
import './i18n'
7+
8+
ReactDOM.render(<App />, document.getElementById('root'))

src/frameworks/custom.ts

+35-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import path from 'path'
22
import fs from 'fs'
3-
import { workspace, FileSystemWatcher } from 'vscode'
3+
import { workspace, FileSystemWatcher, TextDocument } from 'vscode'
44
import YAML from 'js-yaml'
5-
import { Framework } from './base'
5+
import { Framework, ScopeRange } from './base'
66
import { Global } from '~/core'
77
import { LanguageId, File, Log } from '~/utils'
88

@@ -11,6 +11,7 @@ const CustomFrameworkConfigFilename = './.vscode/i18n-ally-custom-framework.yml'
1111
interface CustomFrameworkConfig {
1212
languageIds?: LanguageId[] | LanguageId
1313
usageMatchRegex?: string[] | string
14+
scopeRangeRegex?: string
1415
refactorTemplates?: string[]
1516
monopoly?: boolean
1617

@@ -81,6 +82,38 @@ class CustomFramework extends Framework {
8182
.map(i => i.replace(/\$1/g, keypath))
8283
}
8384

85+
getScopeRange(document: TextDocument): ScopeRange[] | undefined {
86+
if (!this.data?.scopeRangeRegex)
87+
return undefined
88+
89+
if (!this.languageIds.includes(document.languageId as any))
90+
return
91+
92+
const ranges: ScopeRange[] = []
93+
const text = document.getText()
94+
const reg = new RegExp(this.data.scopeRangeRegex, 'g')
95+
96+
for (const match of text.matchAll(reg)) {
97+
if (match?.index == null)
98+
continue
99+
100+
// end previous scope
101+
if (ranges.length)
102+
ranges[ranges.length - 1].end = match.index
103+
104+
// start new scope if namespace provides
105+
if (match[1]) {
106+
ranges.push({
107+
start: match.index,
108+
end: text.length,
109+
namespace: match[1] as string,
110+
})
111+
}
112+
}
113+
114+
return ranges
115+
}
116+
84117
startWatch(root?: string) {
85118
if (this.watchingFor) {
86119
this.watchingFor = undefined

0 commit comments

Comments
 (0)