Created
April 14, 2021 20:59
-
-
Save emiloberg/5b44885b2aeb90604f6b6f87c8eada8f to your computer and use it in GitHub Desktop.
Extract translations from Typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Finds and prints all usage of t() | |
* Use to extract translations | |
* Save as file and run: | |
* ts-node extract-translations.ts | |
* | |
* Change ROOT_PATH in this file if needed. | |
*/ | |
import { readFileSync, readdirSync, statSync } from 'fs'; | |
import { join as joinPath, parse as parsePath } from 'path'; | |
import { | |
createSourceFile, | |
ScriptTarget, | |
Node, | |
SyntaxKind, | |
CallExpression, | |
StringLiteral, | |
PropertyAccessExpression, | |
Identifier, | |
TemplateExpression, | |
ScriptKind, | |
} from 'typescript'; | |
const ROOT_PATH = joinPath(__dirname, './src'); | |
const getAllFiles = (dirPath: string): string[] => { | |
const out: string[] = []; | |
const traverseDir = (dir: string): void => { | |
for (const file of readdirSync(dir)) { | |
const absPath = joinPath(dir, file); | |
if (statSync(absPath).isDirectory()) { | |
traverseDir(absPath); | |
} else { | |
const ext = parsePath(absPath).ext; | |
if (ext === '.ts' || ext === '.tsx') { | |
out.push(absPath); | |
} | |
} | |
} | |
}; | |
traverseDir(dirPath); | |
return out; | |
}; | |
const isType = <T>(obj: any, kind: SyntaxKind): obj is T => obj.kind === kind; | |
const getTranslationKeys = (paths: string[]): { keys: Set<string>; badKeys: Set<string> } => { | |
const foundKeys = new Set<string>(); | |
const foundBadKeys = new Set<string>(); | |
const traverseTree = (node: Node, path: string) => { | |
if (isType<CallExpression>(node, SyntaxKind.CallExpression)) { | |
if ( | |
(isType<PropertyAccessExpression>(node.expression, SyntaxKind.PropertyAccessExpression) && | |
node.expression.name.escapedText === 't') || | |
(isType<Identifier>(node.expression, SyntaxKind.Identifier) && node.expression.escapedText === 't') | |
) { | |
const arg = node.arguments[0]; | |
if (isType<StringLiteral>(arg, SyntaxKind.StringLiteral)) { | |
foundKeys.add(arg.text); | |
} else if (isType<TemplateExpression>(arg, SyntaxKind.TemplateExpression)) { | |
foundBadKeys.add(`Template key ${arg.getFullText()} found in ${path}`); | |
} else if (isType<Identifier>(arg, SyntaxKind.Identifier)) { | |
foundBadKeys.add(`Variable '${arg.getText()}' found in ${path}`); | |
} else if (isType<PropertyAccessExpression>(arg, SyntaxKind.PropertyAccessExpression)) { | |
foundBadKeys.add(`Property access '${arg.getText()}' found in ${path}`); | |
} else { | |
foundBadKeys.add(`Unhandled argument of type ${SyntaxKind[arg.kind]} in file ${path}`); | |
} | |
} | |
} | |
node.forEachChild((childNode) => traverseTree(childNode, path)); | |
}; | |
for (const path of paths) { | |
const fileStr = readFileSync(path, 'utf-8'); | |
const extension = path.split('.')[path.split('.').length - 1]; | |
let scriptKind; | |
if (extension === 'tsx') { | |
scriptKind = ScriptKind.TSX; | |
} else if (extension === 'ts') { | |
scriptKind = ScriptKind.TS; | |
} | |
const ast = createSourceFile('apa.boll', fileStr, ScriptTarget.ES2015, true, scriptKind); | |
traverseTree(ast, path); | |
} | |
return { | |
keys: foundKeys, | |
badKeys: foundBadKeys, | |
}; | |
}; | |
console.log(); | |
console.log(); | |
console.log(`Looking for keys in all files in ${ROOT_PATH}`); | |
console.log(); | |
console.log(); | |
const filePaths = getAllFiles(ROOT_PATH); | |
const { keys, badKeys } = getTranslationKeys(filePaths); | |
for (const key of keys) { | |
console.log(key); | |
} | |
console.log(); | |
console.log(); | |
for (const key of badKeys) { | |
console.warn(key); | |
} | |
console.log(); | |
console.log(); | |
console.log(`Found ${keys.size} keys`); | |
console.log(`Found ${badKeys.size} BAD keys`); | |
console.log(); | |
console.log(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment