Skip to content

Commit 5def84b

Browse files
heiskrV-halfaro
andauthored
Fix always-English data path separators (#63504)
Co-authored-by: Hector A. <v-halfaro@github.com> Copilot-Session: 7c52b57f-2c29-4aa1-8233-99d0d6e13571
1 parent a110e0b commit 5def84b

2 files changed

Lines changed: 85 additions & 23 deletions

File tree

‎src/data-directory/lib/get-data.ts‎

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,34 @@ export const getDeepDataByLanguage = memoize(
4343
if (dir === null) {
4444
dir = languages[langCode].dir
4545
}
46-
return getDeepDataByDir(dottedPath, dir)
46+
const englishRoot = langCode === 'en' ? dir : languages.en.dir
47+
return getDeepDataByDir(dottedPath, dir, englishRoot)
4748
},
4849
)
4950

50-
// getDeepDataByLanguage caches each top-level path, so recursive reads need no extra cache.
51-
function getDeepDataByDir(dottedPath: string, dir: string): Record<string, unknown> {
51+
// Doesn't need to be memoized because it's used by getDataKeysByLanguage
52+
// which is already memoized.
53+
function getDeepDataByDir(
54+
dottedPath: string,
55+
dir: string,
56+
englishRoot: string,
57+
): Record<string, unknown> {
5258
const fullPath = ['data']
5359
const split = dottedPath.split(/\./g)
5460
fullPath.push(...split)
5561

5662
const things: Record<string, unknown> = {}
57-
const relPath = fullPath.join(path.sep)
63+
const relPath = path.posix.join(...fullPath)
5864
for (const dirent of getDirents(dir, relPath)) {
5965
if (dirent.name === 'README.md') continue
6066
// Release-note basenames like '3-5' and '0-rc2' stay intact.
6167
const key = dirent.isDirectory() ? dirent.name : dirent.name.replace(/\.yml$/, '')
6268
if (dirent.isDirectory()) {
63-
things[key] = getDeepDataByDir(`${dottedPath}.${key}`, dir)
69+
things[key] = getDeepDataByDir(`${dottedPath}.${key}`, dir, englishRoot)
6470
} else if (dirent.name.endsWith('.yml')) {
65-
things[key] = getYamlContent(dir, path.join(relPath, dirent.name))
71+
things[key] = getYamlContent(dir, path.posix.join(relPath, dirent.name), englishRoot)
6672
} else if (dirent.name.endsWith('.md')) {
67-
things[key] = getMarkdownContent(dir, path.join(relPath, dirent.name))
73+
things[key] = getMarkdownContent(dir, path.posix.join(relPath, dirent.name), englishRoot)
6874
} else {
6975
throw new Error(`don't know how to read '${dirent.name}'`)
7076
}
@@ -91,7 +97,7 @@ export const getUIDataMerged = memoize((langCode: string): UIStrings => {
9197
const getUIData = (langCode: string): Record<string, unknown> => {
9298
const fullPath = ['data', 'ui.yml']
9399
const { dir } = languages[langCode]
94-
return getYamlContent(dir, fullPath.join(path.sep)) as Record<string, unknown>
100+
return getYamlContent(dir, path.posix.join(...fullPath)) as Record<string, unknown>
95101
}
96102

97103
// When translated data misses a dotted path, retry English.
@@ -105,7 +111,7 @@ export const getDataByLanguage = memoize((dottedPath: string, langCode: string):
105111
const value = getDataByDir(dottedPath, dir, languages.en.dir, langCode)
106112

107113
if (value === undefined && langCode !== 'en') {
108-
return getDataByDir(dottedPath, languages.en.dir)
114+
return getDataByDir(dottedPath, languages.en.dir, languages.en.dir)
109115
}
110116
return value
111117
} catch (error) {
@@ -115,7 +121,8 @@ export const getDataByLanguage = memoize((dottedPath: string, langCode: string):
115121
if (DEBUG_JIT_DATA_READS) {
116122
logger.warn('Unable to parse Yaml in translation', { langCode, dottedPath, error })
117123
}
118-
return getDataByDir(dottedPath, languages.en.dir)
124+
// Give it one more chance, but use English this time
125+
return getDataByDir(dottedPath, languages.en.dir, languages.en.dir)
119126
}
120127
// Throw English YAML errors so staff writers see corrupt source data early.
121128
throw error
@@ -152,21 +159,18 @@ function getDataByDir(
152159
const basename = split.pop()!
153160
fullPath.push(...split)
154161
fullPath.push(`${basename}.yml`)
155-
const allData = getYamlContent(dir, fullPath.join(path.sep), englishRoot) as
156-
| Record<string, unknown>
157-
| undefined
162+
const relPath = path.posix.join(...fullPath)
163+
const allData = getYamlContent(dir, relPath, englishRoot) as Record<string, unknown> | undefined
158164
if (allData && key) {
159165
const value = allData[key]
160166
if (value) {
161167
let content = matter(value as string).content
162168
if (dir !== englishRoot) {
163169
let englishContent = content
164170
try {
165-
const englishData = getYamlContent(
166-
englishRoot,
167-
fullPath.join(path.sep),
168-
englishRoot,
169-
) as Record<string, unknown> | undefined
171+
const englishData = getYamlContent(englishRoot, relPath, englishRoot) as
172+
| Record<string, unknown>
173+
| undefined
170174
if (englishData?.[key]) {
171175
englishContent = matter(englishData[key] as string).content
172176
}
@@ -183,7 +187,7 @@ function getDataByDir(
183187
return content
184188
}
185189
} else {
186-
logger.warn('Unable to find variables Yaml file', { filePath: fullPath.join(path.sep) })
190+
logger.warn('Unable to find variables Yaml file', { filePath: relPath })
187191
}
188192
return undefined
189193
}
@@ -192,13 +196,14 @@ function getDataByDir(
192196
const nakedname = split.pop()!
193197
fullPath.push(...split)
194198
fullPath.push(`${nakedname}.md`)
195-
const markdown = getMarkdownContent(dir, fullPath.join(path.sep), englishRoot)
199+
const relPath = path.posix.join(...fullPath)
200+
const markdown = getMarkdownContent(dir, relPath, englishRoot)
196201
let { content } = matter(markdown)
197202
if (dir !== englishRoot) {
198203
// Translated reusables need English content to fix corruptions like [AUTOTITLE"을](/foo/bar).
199204
let englishContent = content
200205
try {
201-
englishContent = getMarkdownContent(englishRoot, fullPath.join(path.sep), englishRoot)
206+
englishContent = getMarkdownContent(englishRoot, relPath, englishRoot)
202207
} catch (error) {
203208
// Translated pages can reference reusables missing in English; other corrections still run.
204209
if ((error as FileSystemError).code !== 'ENOENT') {
@@ -217,15 +222,15 @@ function getDataByDir(
217222
if (first === 'ui') {
218223
const basename = split.shift()
219224
fullPath.push(`${basename}.yml`)
220-
const allData = getYamlContent(dir, fullPath.join(path.sep), englishRoot)
225+
const allData = getYamlContent(dir, path.posix.join(...fullPath), englishRoot)
221226
return get(allData, split.join('.'))
222227
}
223228

224229
if (first === 'glossaries' || first === 'release-notes') {
225230
const basename = split.pop()!
226231
fullPath.push(...split)
227232
fullPath.push(`${basename}.yml`)
228-
return getYamlContent(dir, fullPath.join(path.sep), englishRoot)
233+
return getYamlContent(dir, path.posix.join(...fullPath), englishRoot)
229234
}
230235

231236
throw new Error(`Can't find the key '${dottedPath}' in the scope.`)

‎src/data-directory/tests/get-data.ts‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ describe('get-data', () => {
2828
},
2929
},
3030
variables: {
31+
copilot: {
32+
prodname_copilot: 'GitHub Copilot',
33+
},
34+
product: {
35+
company_short: 'GitHub',
36+
},
3137
stuff: {
3238
foo: 'Foo',
3339
bar: 'Bar',
@@ -36,6 +42,10 @@ describe('get-data', () => {
3642
reusables: {
3743
coolness: 'This is *Markdown*',
3844
otherness: '**Also** Markdown',
45+
ssh: {
46+
fingerprints: 'English fingerprints',
47+
known_hosts: 'English known hosts',
48+
},
3949
},
4050
},
4151
})
@@ -54,12 +64,22 @@ describe('get-data', () => {
5464
},
5565
},
5666
variables: {
67+
copilot: {
68+
prodname_copilot: 'Translated Copilot',
69+
},
70+
product: {
71+
company_short: 'Translated GitHub',
72+
},
5773
stuff: {
5874
foo: 'フー',
5975
},
6076
},
6177
reusables: {
6278
coolness: 'これがマークダウンです',
79+
ssh: {
80+
fingerprints: 'Translated fingerprints',
81+
known_hosts: 'Translated known hosts',
82+
},
6383
},
6484
},
6585
},
@@ -102,6 +122,17 @@ describe('get-data', () => {
102122
}
103123
})
104124

125+
test('getDataByLanguage always reads selected variable data from English', () => {
126+
{
127+
const result = getDataByLanguage('variables.product.company_short', 'ja')
128+
expect(result).toBe('GitHub')
129+
}
130+
{
131+
const result = getDataByLanguage('variables.copilot.prodname_copilot', 'ja')
132+
expect(result).toBe('GitHub Copilot')
133+
}
134+
})
135+
105136
test('getDataByLanguage variables failures', () => {
106137
{
107138
const result = getDataByLanguage('variables.stuff.key_non_existent', 'en')
@@ -117,6 +148,11 @@ describe('get-data', () => {
117148
}
118149
})
119150

151+
test('getDataByLanguage uses configured English root for always-English missing keys', () => {
152+
const result = getDataByLanguage('variables.product.prodname_dotcom', 'ja')
153+
expect(result).toBeUndefined()
154+
})
155+
120156
test('getDataByLanguage reusables English', () => {
121157
{
122158
const result = getDataByLanguage('reusables.coolness', 'en')
@@ -139,6 +175,17 @@ describe('get-data', () => {
139175
}
140176
})
141177

178+
test('getDataByLanguage always reads selected SSH reusables from English', () => {
179+
{
180+
const result = getDataByLanguage('reusables.ssh.fingerprints', 'ja')
181+
expect(result).toBe('English fingerprints')
182+
}
183+
{
184+
const result = getDataByLanguage('reusables.ssh.known_hosts', 'ja')
185+
expect(result).toBe('English known hosts')
186+
}
187+
})
188+
142189
test('getDataByLanguage failures', () => {
143190
{
144191
const result = getDataByLanguage('reusables.neverheardof', 'en')
@@ -175,6 +222,16 @@ describe('get-data', () => {
175222
expect(result['coolness.md']).toBe('This is *Markdown*')
176223
}
177224
})
225+
226+
test('getDeepDataByLanguage uses configured English root for always-English files', () => {
227+
const variables = getDeepDataByLanguage('variables', 'en')
228+
expect((variables.product as Record<string, string>).prodname_dotcom).toBeUndefined()
229+
expect((variables.copilot as Record<string, string>).prodname_copilot).toBe('GitHub Copilot')
230+
231+
const sshReusables = getDeepDataByLanguage('reusables.ssh', 'en')
232+
expect(sshReusables['fingerprints.md']).toBe('English fingerprints')
233+
expect(sshReusables['known_hosts.md']).toBe('English known hosts')
234+
})
178235
})
179236

180237
const VALID_ENGLISH_MARKDOWN = `

0 commit comments

Comments
 (0)