-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[core] Prettify the l10n issue #4928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
439d1cd
1b2786b
a6fd373
6840bef
b884ed5
f452dc2
4d0ee0a
c0082c4
4ac3df1
3ae514a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -16,6 +16,21 @@ const GIT_REPO = 'mui-x'; | |||||
const L10N_ISSUE_ID = 3211; | ||||||
const SOURCE_CODE_REPO = `https://github.com/${GIT_ORGANIZATION}/${GIT_REPO}`; | ||||||
|
||||||
const packagesWithL10n = [ | ||||||
{ | ||||||
key: 'data-grid', | ||||||
reportName: '๐งโ๐ผ DataGrid, DataGridPro, DataGridPremium', | ||||||
constantsRelativePath: 'packages/grid/x-data-grid/src/constants/localeTextConstants.ts', | ||||||
localesRelativePath: 'packages/grid/x-data-grid/src/locales', | ||||||
}, | ||||||
{ | ||||||
key: 'pickers', | ||||||
reportName: '๐ Date and Time Pickers', | ||||||
constantsRelativePath: 'packages/x-date-pickers/src/locales/enUS.ts', | ||||||
localesRelativePath: 'packages/x-date-pickers/src/locales', | ||||||
}, | ||||||
]; | ||||||
|
||||||
const BABEL_PLUGINS = [require.resolve('@babel/plugin-syntax-typescript')]; | ||||||
|
||||||
type Translations = Record<string, babelTypes.Node>; | ||||||
|
@@ -46,8 +61,8 @@ function plugin(existingTranslations: Translations): babel.PluginObj { | |||||
return; | ||||||
} | ||||||
|
||||||
// Test if the variable name follows the pattern xxXXGrid | ||||||
if (!/[a-z]{2}[A-Z]{2}Grid/.test(node.id.name)) { | ||||||
// Test if the variable name follows the pattern xxXXGrid or xxXXPickers | ||||||
if (!/[a-z]{2}[A-Z]{2}(Grid|Pickers)/.test(node.id.name)) { | ||||||
visitorPath.skip(); | ||||||
return; | ||||||
} | ||||||
|
@@ -133,7 +148,7 @@ function extractTranslations(translationsPath: string): [TranslationsByGroup, Tr | |||||
return [translationsByGroup, translations]; | ||||||
} | ||||||
|
||||||
function findLocales(localesDirectory: string) { | ||||||
function findLocales(localesDirectory: string, constantsPath: string) { | ||||||
const items = fse.readdirSync(localesDirectory); | ||||||
const locales: any[] = []; | ||||||
const localeRegex = /^[a-z]{2}[A-Z]{2}/; | ||||||
|
@@ -146,7 +161,10 @@ function findLocales(localesDirectory: string) { | |||||
|
||||||
const localePath = path.resolve(localesDirectory, item); | ||||||
const code = match[0]; | ||||||
locales.push([localePath, code]); | ||||||
if (constantsPath !== localePath) { | ||||||
// Ignore the locale used as a reference | ||||||
locales.push([localePath, code]); | ||||||
} | ||||||
}); | ||||||
|
||||||
return locales; | ||||||
|
@@ -201,19 +219,65 @@ function countryToFlag(isoCode: string) { | |||||
: isoCode; | ||||||
} | ||||||
|
||||||
async function generateReport( | ||||||
missingTranslations: Record<string, { path: string; locations: number[] }>, | ||||||
) { | ||||||
interface MissingKey { | ||||||
currentLineContent: string; | ||||||
lineIndex: number; | ||||||
} | ||||||
interface MissingTranslations { | ||||||
alexfauquette marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
[localeCode: string]: { | ||||||
[packageCode: string]: { | ||||||
path: string; | ||||||
missingKeys: MissingKey[]; | ||||||
}; | ||||||
}; | ||||||
} | ||||||
|
||||||
async function generateReport(missingTranslations: MissingTranslations) { | ||||||
const lastCommitRef = await git('log -n 1 --pretty="format:%H"'); | ||||||
const lines: string[] = []; | ||||||
Object.entries(missingTranslations).forEach(([code, info]) => { | ||||||
if (info.locations.length === 0) { | ||||||
return; | ||||||
} | ||||||
lines.push(`### ${countryToFlag(code.slice(2))} ${code.slice(0, 2)}-${code.slice(2)}`); | ||||||
info.locations.forEach((location) => { | ||||||
const permalink = `${SOURCE_CODE_REPO}/blob/${lastCommitRef}/${info.path}#L${location}`; | ||||||
lines.push(permalink); | ||||||
Object.entries(missingTranslations).forEach(([languageCode, infoPerPackage]) => { | ||||||
lines.push(''); | ||||||
lines.push( | ||||||
`### ${countryToFlag(languageCode.slice(2))} ${languageCode.slice(0, 2)}-${languageCode.slice( | ||||||
2, | ||||||
)}`, | ||||||
); | ||||||
|
||||||
packagesWithL10n.forEach(({ key: packageKey, reportName, localesRelativePath }) => { | ||||||
const info = infoPerPackage[packageKey]; | ||||||
|
||||||
lines.push('<details>'); | ||||||
|
||||||
const fileName = `${languageCode.slice(0, 2).toLowerCase()}${languageCode | ||||||
.slice(2) | ||||||
.toUpperCase()}.ts`; | ||||||
const filePath = `${localesRelativePath}/${fileName}`; | ||||||
if (!info) { | ||||||
lines.push(` <summary>${reportName}: file to create </summary>`); | ||||||
alexfauquette marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
lines.push(''); | ||||||
lines.push(` > Add file \`${filePath}\` to start contribution to this locale`); | ||||||
alexfauquette marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} else if (info.missingKeys.length === 0) { | ||||||
lines.push(` <summary>${reportName} (Done โ )</summary>`); | ||||||
lines.push(''); | ||||||
lines.push(` > This locale has been completed by the community ๐`); | ||||||
lines.push( | ||||||
` > You can still look for typo fix or improvements in [the translation file](${SOURCE_CODE_REPO}/blob/${lastCommitRef}/${filePath}) ๐ต`, | ||||||
); | ||||||
} else { | ||||||
lines.push(` <summary>${reportName}(${info.missingKeys.length} remaining)</summary>`); | ||||||
alexfauquette marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
lines.push(''); | ||||||
info.missingKeys.forEach((missingKey) => { | ||||||
const permalink = `${SOURCE_CODE_REPO}/blob/${lastCommitRef}/${info.path}#L${missingKey.lineIndex}`; | ||||||
let lineContent = missingKey.currentLineContent; | ||||||
|
||||||
if (lineContent[lineContent.length - 1] === ',') { | ||||||
lineContent = lineContent.slice(0, lineContent.length - 1); | ||||||
} | ||||||
lines.push(` - [\`${lineContent}\`](${permalink})`); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to escape ``. The line content may contain backstick when string interpolation is used, e.g.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, I did not know about this space technic. Seems to work well ๐ฝ ๐บ๐ฆ uk-UA๐งโ๐ผ DataGrid, DataGridPro, DataGridPremium(16 remaining)
๐ Date and Time Pickers: file to create
|
||||||
}); | ||||||
} | ||||||
|
||||||
lines.push('</details>'); | ||||||
}); | ||||||
}); | ||||||
return lines.join('\n'); | ||||||
|
@@ -229,7 +293,6 @@ async function updateIssue(githubToken, newMessage) { | |||||
|
||||||
Run \`yarn l10n --report\` to update the list below โฌ๏ธ | ||||||
|
||||||
## DataGrid / DataGridPro | ||||||
${newMessage} | ||||||
`; | ||||||
await octokit | ||||||
|
@@ -256,64 +319,72 @@ async function run(argv: yargs.ArgumentsCamelCase<HandlerArgv>) { | |||||
const { report, githubToken } = argv; | ||||||
const workspaceRoot = path.resolve(__dirname, '../'); | ||||||
|
||||||
const constantsPath = path.join( | ||||||
workspaceRoot, | ||||||
'packages/grid/x-data-grid/src/constants/localeTextConstants.ts', | ||||||
); | ||||||
const [baseTranslationsByGroup, baseTranslations] = extractTranslations(constantsPath); | ||||||
const missingTranslations: Record<string, any> = {}; | ||||||
|
||||||
const localesDirectory = path.resolve(workspaceRoot, 'packages/grid/x-data-grid/src/locales'); | ||||||
const locales = findLocales(localesDirectory); | ||||||
packagesWithL10n.forEach((packageInfo) => { | ||||||
const constantsPath = path.join(workspaceRoot, packageInfo.constantsRelativePath); | ||||||
const [baseTranslationsByGroup, baseTranslations] = extractTranslations(constantsPath); | ||||||
|
||||||
const missingTranslations: Record<string, any> = {}; | ||||||
const localesDirectory = path.resolve(workspaceRoot, packageInfo.localesRelativePath); | ||||||
const locales = findLocales(localesDirectory, constantsPath); | ||||||
|
||||||
locales.forEach(([localePath, localeCode]) => { | ||||||
const { | ||||||
translations: existingTranslations, | ||||||
transformedCode, | ||||||
rawCode, | ||||||
} = extractAndReplaceTranslations(localePath); | ||||||
locales.forEach(([localePath, localeCode]) => { | ||||||
const { | ||||||
translations: existingTranslations, | ||||||
transformedCode, | ||||||
rawCode, | ||||||
} = extractAndReplaceTranslations(localePath); | ||||||
|
||||||
if (!transformedCode || Object.keys(existingTranslations).length === 0) { | ||||||
return; | ||||||
} | ||||||
if (!transformedCode || Object.keys(existingTranslations).length === 0) { | ||||||
return; | ||||||
} | ||||||
|
||||||
const codeWithNewTranslations = injectTranslations( | ||||||
transformedCode, | ||||||
existingTranslations, | ||||||
baseTranslationsByGroup, | ||||||
); | ||||||
const codeWithNewTranslations = injectTranslations( | ||||||
transformedCode, | ||||||
existingTranslations, | ||||||
baseTranslationsByGroup, | ||||||
); | ||||||
|
||||||
const prettierConfigPath = path.join(workspaceRoot, 'prettier.config.js'); | ||||||
const prettierConfig = prettier.resolveConfig.sync(localePath, { config: prettierConfigPath }); | ||||||
const prettierConfigPath = path.join(workspaceRoot, 'prettier.config.js'); | ||||||
const prettierConfig = prettier.resolveConfig.sync(localePath, { | ||||||
config: prettierConfigPath, | ||||||
}); | ||||||
|
||||||
const prettifiedCode = prettier.format(codeWithNewTranslations, { | ||||||
...prettierConfig, | ||||||
filepath: localePath, | ||||||
}); | ||||||
const prettifiedCode = prettier.format(codeWithNewTranslations, { | ||||||
...prettierConfig, | ||||||
filepath: localePath, | ||||||
}); | ||||||
|
||||||
const lines = rawCode.split('\n'); | ||||||
Object.entries(baseTranslations).forEach(([key]) => { | ||||||
if (!existingTranslations[key]) { | ||||||
if (!missingTranslations[localeCode]) { | ||||||
missingTranslations[localeCode] = { | ||||||
path: localePath.replace(workspaceRoot, '').slice(1), // Remove leading slash | ||||||
locations: [], | ||||||
}; | ||||||
} | ||||||
const location = lines.findIndex((line) => line.trim().startsWith(`// ${key}:`)); | ||||||
// Ignore when both the translation and the placeholder are missing | ||||||
if (location >= 0) { | ||||||
missingTranslations[localeCode].locations.push(location + 1); | ||||||
// We always set the `locations` to [] such that we can differentiate translation completed from un-existing translations | ||||||
if (!missingTranslations[localeCode]) { | ||||||
missingTranslations[localeCode] = {}; | ||||||
} | ||||||
if (!missingTranslations[localeCode][packageInfo.key]) { | ||||||
missingTranslations[localeCode][packageInfo.key] = { | ||||||
path: localePath.replace(workspaceRoot, '').slice(1), // Remove leading slash | ||||||
missingKeys: [], | ||||||
}; | ||||||
} | ||||||
const lines = rawCode.split('\n'); | ||||||
Object.entries(baseTranslations).forEach(([key]) => { | ||||||
if (!existingTranslations[key]) { | ||||||
const location = lines.findIndex((line) => line.trim().startsWith(`// ${key}:`)); | ||||||
// Ignore when both the translation and the placeholder are missing | ||||||
if (location >= 0) { | ||||||
missingTranslations[localeCode][packageInfo.key].missingKeys.push({ | ||||||
currentLineContent: lines[location].trim().slice(3), | ||||||
lineIndex: location + 1, | ||||||
}); | ||||||
} | ||||||
} | ||||||
}); | ||||||
|
||||||
if (!report) { | ||||||
fse.writeFileSync(localePath, prettifiedCode); | ||||||
// eslint-disable-next-line no-console | ||||||
console.log(`Wrote ${localeCode} locale.`); | ||||||
} | ||||||
}); | ||||||
|
||||||
if (!report) { | ||||||
fse.writeFileSync(localePath, prettifiedCode); | ||||||
// eslint-disable-next-line no-console | ||||||
console.log(`Wrote ${localeCode} locale.`); | ||||||
} | ||||||
}); | ||||||
|
||||||
if (report) { | ||||||
|
Uh oh!
There was an error while loading. Please reload this page.