Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <slug>`: Specifies the plugin or theme slug.
- `--domain <domain>`: Specifies the text domain to look for in the source code.
- `--domain <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.
Expand All @@ -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
Expand Down
7 changes: 6 additions & 1 deletion src/cli/getArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down Expand Up @@ -117,6 +118,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",
Expand Down
1 change: 1 addition & 0 deletions src/cli/parseCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion src/parser/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.debug, args),
);
if (fileTree) {
tasks.push(fileTree as Promise<SetOfBlocks>);
Expand Down
39 changes: 26 additions & 13 deletions src/parser/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -36,12 +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,
debugEnabled?: boolean,
args?: Args,
): SetOfBlocks {
// set up the parser
const parser = new Parser();
Expand Down Expand Up @@ -112,8 +115,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];
Expand All @@ -137,31 +145,36 @@ 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;

// increment the index of the translation key
translationKeyIndex += 1;
}

if (Array.isArray(args?.options?.translationDomains) && !args.options.translationDomains.includes(translation.text_domain as string)) {
return;
}

const comments = collectComments(argsNode);

// Get the translation data
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export interface Args {
themeJson?: boolean;
audit?: boolean;
};
translationDomains?: string[];
};
headers?: { [key in PotHeaders]: string };
patterns: Patterns;
Expand Down
37 changes: 37 additions & 0 deletions tests/tree.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,40 @@ 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 = `<?php

__( 'hello, world' );
__( 'hello, foo', 'foo-plugin' );
__( 'hello, bar', 'bar-plugin' );
`;

const filename = "filename.php";

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);
}
});
});

describe("doTree php _n, _nx", async () => {
it("should extract translations and comments from code content", () => {
const content = `<?php

// translators: %d a number of years
_n( '%d year ago', '%d years go', 1, 'foo-plugin' );

// translators: %d a number of years
_nx( '%d year ago', '%d years go', 1, 'context', 'foo-plugin' );
`;

const filename = "filename.php";

const r = doTree(content, filename).blocks;

assert.strictEqual(r.filter((block) => block.msgctxt === 'context').length, 1);
});
});
Loading