From fb56b061198aaf0162dc36984708f9127b9afe0e Mon Sep 17 00:00:00 2001 From: chrono-meter Date: Wed, 3 Sep 2025 10:03:59 +0900 Subject: [PATCH 1/4] Add command line option `--translation-domains` for filtering translations. --- README.md | 1 + src/cli/getArgs.ts | 4 ++++ src/cli/parseCli.ts | 1 + src/parser/process.ts | 2 +- src/parser/tree.ts | 6 ++++++ src/types.ts | 1 + tests/tree.test.js | 17 +++++++++++++++++ 7 files changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8504bc0..bc21a6b 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ npx @wp-blocks/make-pot src languages --charset='utf-8' --include="src/**/*.{ts, - `--silent`: Suppresses output to stdout. - `--json`: Outputs the JSON gettext data. - `--charset`: Defines the encoding charset of the pot file, you can choose "iso-8859-1" and "uft-8" (defaults to iso-8859-1) +- `--translation-domains`: Restrict to specific translation domains. - `--output`: Outputs the gettext data. ### Example usage diff --git a/src/cli/getArgs.ts b/src/cli/getArgs.ts index d4e14ec..bff95a8 100644 --- a/src/cli/getArgs.ts +++ b/src/cli/getArgs.ts @@ -117,6 +117,10 @@ export function getArgs(userArgs = {}): Args | MakeJsonArgs { type: "string", default: "latin1", }, + "translation-domains": { + describe: "Restrict to specific translation domains", + type: "array", + }, debug: { describe: "Debug mode", type: "boolean", diff --git a/src/cli/parseCli.ts b/src/cli/parseCli.ts index e21ba6f..3f3e51c 100644 --- a/src/cli/parseCli.ts +++ b/src/cli/parseCli.ts @@ -132,6 +132,7 @@ export function parseCliArgs( themeJson: !!args.skipThemeJson, audit: !!args.skipAudit, }, + translationDomains: args.translationDomains ? Array.isArray(args.translationDomains) ? args.translationDomains.map(String) : [String(args.translationDomains)] : undefined, }, // Patterns patterns: { diff --git a/src/parser/process.ts b/src/parser/process.ts index 0c29ac4..889ecff 100644 --- a/src/parser/process.ts +++ b/src/parser/process.ts @@ -48,7 +48,7 @@ export async function processFiles( ); } else if (allowedFormats.includes(ext)) { const fileTree = readFileAsync(fileRealPath).then((content) => - doTree(content, file, args.debug), + doTree(content, file, args.options?.translationDomains, args.debug), ); if (fileTree) { tasks.push(fileTree as Promise); diff --git a/src/parser/tree.ts b/src/parser/tree.ts index 7c3a2c1..a45afc2 100644 --- a/src/parser/tree.ts +++ b/src/parser/tree.ts @@ -41,6 +41,7 @@ function collectComments(node: SyntaxNode): string | undefined { export function doTree( sourceCode: string, filepath: string, + translationDomains?: string[], debugEnabled?: boolean, ): SetOfBlocks { // set up the parser @@ -113,6 +114,7 @@ export function doTree( msgid: string; msgid_plural: string; msgstr: string; + text_domain: string; }> = {}; const translationKeys = @@ -162,6 +164,10 @@ export function doTree( translationKeyIndex += 1; } + if (Array.isArray(translationDomains) && !translationDomains.includes(translation.text_domain as string)) { + return; + } + const comments = collectComments(argsNode); // Get the translation data diff --git a/src/types.ts b/src/types.ts index 4a73891..7796601 100644 --- a/src/types.ts +++ b/src/types.ts @@ -113,6 +113,7 @@ export interface Args { themeJson?: boolean; audit?: boolean; }; + translationDomains?: string[]; }; headers?: { [key in PotHeaders]: string }; patterns: Patterns; diff --git a/tests/tree.test.js b/tests/tree.test.js index f32413d..196ca75 100644 --- a/tests/tree.test.js +++ b/tests/tree.test.js @@ -177,3 +177,20 @@ describe("doTree large file", () => { assert.strictEqual(r.filter((block) => block.comments).length, 19); }); }); + +describe("doTree php filtered by translation domain", async () => { + it("should extract translations filtered by translation domain", () => { + const content = ` block).length, 1); + }); +}); From c2a2b2ad165067f9f5bdf8a7133dbf178e0446cd Mon Sep 17 00:00:00 2001 From: chrono-meter Date: Wed, 3 Sep 2025 10:13:12 +0900 Subject: [PATCH 2/4] Fix to parse `_n' and `_nx`. --- src/parser/tree.ts | 31 ++++++++++++++++++------------- tests/tree.test.js | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/parser/tree.ts b/src/parser/tree.ts index a45afc2..e29fdce 100644 --- a/src/parser/tree.ts +++ b/src/parser/tree.ts @@ -113,9 +113,13 @@ export function doTree( msgctxt: string; msgid: string; msgid_plural: string; + number: string; msgstr: string; text_domain: string; - }> = {}; + }> = { + // WordPress default text domain is 'default' + text_domain: 'default', + }; const translationKeys = i18nFunctions[functionName as keyof typeof i18nFunctions]; @@ -139,24 +143,25 @@ export function doTree( continue; } + // the translation key (eg. msgid) + const currentKey = translationKeys[ + translationKeyIndex + ] as keyof typeof translation; + if (node?.type && stringType.includes(node.type)) { // unquote the strings nodeValue = nodeValue.slice(1, -1); + } else if (currentKey === 'number'){ + // `number` accepts any value, this will not be provided in the POT file + nodeValue = node.text; } else { - if (debugEnabled) { - // Whenever we get an unexpected node type this string is not translatable and should be skipped - console.warn( - `Unexpected node type ${node?.type} identified as ${translationKeys[translationKeyIndex]} with value ${nodeValue} in ${filepath} at ${node.startPosition.row + 1} pos ${node.startPosition.column + 1}`, - ); - } - continue; + // Whenever we get an unexpected node type this string is not translatable and should be skipped + console.error( + `Unexpected node type ${node?.type} identified as ${translationKeys[translationKeyIndex]} with value ${nodeValue} in ${filepath} at ${node.startPosition.row + 1} pos ${node.startPosition.column + 1}`, + ); + return; // Parse error, skip this translation. } - // the translation key (eg. msgid) - const currentKey = translationKeys[ - translationKeyIndex - ] as keyof typeof translation; - // the value of that key translation[currentKey] = nodeValue; diff --git a/tests/tree.test.js b/tests/tree.test.js index 196ca75..ca68f00 100644 --- a/tests/tree.test.js +++ b/tests/tree.test.js @@ -194,3 +194,22 @@ describe("doTree php filtered by translation domain", async () => { assert.strictEqual(r.map((block) => block).length, 1); }); }); + +describe("doTree php _n, _nx", async () => { + it("should extract translations and comments from code content", () => { + const content = ` block.msgctxt === 'context').length, 1); + }); +}); From a6016487e18ea1de7a0c0bd85893e2854949aee8 Mon Sep 17 00:00:00 2001 From: chrono-meter Date: Sun, 14 Sep 2025 15:22:05 +0900 Subject: [PATCH 3/4] Refactor `doTree` function to accept Args and update tests for translation domain filtering. --- src/parser/process.ts | 2 +- src/parser/tree.ts | 6 ++++-- tests/tree.test.js | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/parser/process.ts b/src/parser/process.ts index 889ecff..bfa6308 100644 --- a/src/parser/process.ts +++ b/src/parser/process.ts @@ -48,7 +48,7 @@ export async function processFiles( ); } else if (allowedFormats.includes(ext)) { const fileTree = readFileAsync(fileRealPath).then((content) => - doTree(content, file, args.options?.translationDomains, args.debug), + doTree(content, file, args.debug, args), ); if (fileTree) { tasks.push(fileTree as Promise); diff --git a/src/parser/tree.ts b/src/parser/tree.ts index e29fdce..b52c56a 100644 --- a/src/parser/tree.ts +++ b/src/parser/tree.ts @@ -4,6 +4,7 @@ import { i18nFunctions } from "../const.js"; import { Block, SetOfBlocks } from "gettext-merger"; import { getParser } from "../fs/glob.js"; import { reverseSlashes, stripTranslationMarkup } from "../utils/common.js"; +import { Args } from "../types.js"; /** * Collect comments from the AST node and its preceding siblings. @@ -36,13 +37,14 @@ function collectComments(node: SyntaxNode): string | undefined { * @param {string} sourceCode - The source code to be parsed. * @param {string} filepath - The path to the file being parsed. * @param {boolean} debugEnabled - Whether debug mode is enabled. + * @param {Args} args - The command line arguments, optional. * @return {SetOfBlocks} An array of translation strings. */ export function doTree( sourceCode: string, filepath: string, - translationDomains?: string[], debugEnabled?: boolean, + args?: Args, ): SetOfBlocks { // set up the parser const parser = new Parser(); @@ -169,7 +171,7 @@ export function doTree( translationKeyIndex += 1; } - if (Array.isArray(translationDomains) && !translationDomains.includes(translation.text_domain as string)) { + if (Array.isArray(args?.options?.translationDomains) && !args.options.translationDomains.includes(translation.text_domain as string)) { return; } diff --git a/tests/tree.test.js b/tests/tree.test.js index ca68f00..a38da79 100644 --- a/tests/tree.test.js +++ b/tests/tree.test.js @@ -189,9 +189,10 @@ describe("doTree php filtered by translation domain", async () => { const filename = "filename.php"; - const r = doTree(content, filename, ['foo-plugin']).blocks; - - assert.strictEqual(r.map((block) => block).length, 1); + for (const translationDomain of ['default', 'foo-plugin', 'bar-plugin']) { + const r = doTree(content, filename, undefined, { options: { translationDomains: [translationDomain] } }).blocks; + assert.strictEqual(r.map((block) => block).length, 1); + } }); }); From 78aed7f2fd3ae7b58e79b2eb9a20b6492354d31c Mon Sep 17 00:00:00 2001 From: chrono-meter Date: Sun, 14 Sep 2025 15:27:17 +0900 Subject: [PATCH 4/4] Update README and `getArgs` to clarify `domain` options for command line --- README.md | 2 +- src/cli/getArgs.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc21a6b..5cf9b92 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ npx @wp-blocks/make-pot src languages --charset='utf-8' --include="src/**/*.{ts, - `--version`: Displays the version number of `make-pot`. - `-h`, `--help`: Displays help information. - `--slug `: Specifies the plugin or theme slug. -- `--domain `: Specifies the text domain to look for in the source code. +- `--domain `: Specifies the text domain to look for in the source code, you can choose "plugin", "theme", "block", "theme-block", "generic". - `--skip-js`: Skips JavaScript files during processing. - `--skip-php`: Skips PHP files during processing. - `--skip-blade`: Skips Blade files during processing. diff --git a/src/cli/getArgs.ts b/src/cli/getArgs.ts index bff95a8..74b2901 100644 --- a/src/cli/getArgs.ts +++ b/src/cli/getArgs.ts @@ -29,7 +29,8 @@ export function getArgs(userArgs = {}): Args | MakeJsonArgs { type: "string", }, domain: { - describe: "Text domain to look for in the source code", + describe: "Text domain to look for in the source code. Valid domains are: plugin, theme, block, theme-block, generic.", + choices: ["plugin", "theme", "block", "theme-block", "generic"], type: "string", }, "skip-js": {