@@ -35,7 +35,7 @@ describe("doTree php", () => {
3535 translator : undefined ,
3636 } ,
3737 msgctxt : undefined ,
38- msgid : "You\\ 're a silly monkey" ,
38+ msgid : "You're a silly monkey" ,
3939 msgid_plural : undefined ,
4040 msgstr : [ "" ] ,
4141 } ) ;
@@ -214,3 +214,76 @@ describe("doTree php _n, _nx", async () => {
214214 assert . strictEqual ( r . filter ( ( block ) => block . msgctxt === 'context' ) . length , 1 ) ;
215215 } ) ;
216216} ) ;
217+
218+ describe ( "doTree php escape sequences" , async ( ) => {
219+ it ( "should correctly unescape newlines, tabs, and quotes in double-quoted strings" , ( ) => {
220+ const content = `<?php
221+ // Double quotes with escape sequences
222+ __("Line 1\\nLine 2", "text-domain");
223+
224+ // Double quotes with escaped quotes
225+ _e("Hello \\"World\\"", "text-domain");
226+
227+ // Double quotes with tabs
228+ __("Col1\\tCol2", "text-domain");
229+ ` ;
230+
231+ const filename = "escapes.php" ;
232+ const r = doTree ( content , filename ) . blocks ;
233+
234+ // 1. Verify newline
235+ const newlineBlock = r . find ( b => b . msgid . includes ( 'Line 1' ) ) ;
236+ // The msgid should contain an actual newline character, not the literal characters '\' and 'n'
237+ assert . strictEqual ( newlineBlock ?. msgid , "Line 1\nLine 2" ) ;
238+
239+ // 2. Verify escaped quotes
240+ const quoteBlock = r . find ( b => b . msgid . includes ( 'Hello' ) ) ;
241+ assert . strictEqual ( quoteBlock ?. msgid , 'Hello "World"' ) ;
242+
243+ // 3. Verify tabs
244+ const tabBlock = r . find ( b => b . msgid . includes ( 'Col1' ) ) ;
245+ assert . strictEqual ( tabBlock ?. msgid , "Col1\tCol2" ) ;
246+ } ) ;
247+ } ) ;
248+
249+ describe ( "doTree php single quotes" , async ( ) => {
250+ it ( "should treat escape sequences as literals in single-quoted strings" , ( ) => {
251+ const content = `<?php
252+ // Single quotes should NOT interpret \\n as newline
253+ __('Line 1\\nLine 2', 'text-domain');
254+
255+ // Single quotes SHOULD handle escaped single quotes
256+ __('It\\'s a sunny day', 'text-domain');
257+ ` ;
258+
259+ const filename = "single_quotes.php" ;
260+ const r = doTree ( content , filename ) . blocks ;
261+
262+ // 1. Verify literal \\n
263+ const literalBlock = r . find ( b => b . msgid . includes ( 'Line 1' ) ) ;
264+ // In single quotes, \\n is two characters: backslash and n
265+ assert . strictEqual ( literalBlock ?. msgid , "Line 1\\nLine 2" ) ;
266+
267+ // 2. Verify escaped single quote
268+ const quoteBlock = r . find ( b => b . msgid . includes ( 'sunny' ) ) ;
269+ assert . strictEqual ( quoteBlock ?. msgid , "It's a sunny day" ) ;
270+ } ) ;
271+ } ) ;
272+
273+ describe ( "doTree php variables in strings" , async ( ) => {
274+ it ( "should preserve PHP variables inside double-quoted strings" , ( ) => {
275+ const content = `<?php
276+ $name = 'John';
277+ // Variable interpolation
278+ __("Hello $name, how are you?", "text-domain");
279+ ` ;
280+
281+ const filename = "variables.php" ;
282+ const r = doTree ( content , filename ) . blocks ;
283+
284+ const varBlock = r . find ( b => b . msgid . startsWith ( 'Hello' ) ) ;
285+
286+ // We expect the variable name to be preserved in the msgid
287+ assert . strictEqual ( varBlock ?. msgid , "Hello $name, how are you?" ) ;
288+ } ) ;
289+ } ) ;
0 commit comments