From 32f1dc3c9964550ec028666c060fb51ef00114e9 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 11:56:36 -0400
Subject: [PATCH 01/11] garble

---
 frontend/localebuilder/garble.ts            | 63 +++++++++++++++++++++
 frontend/localebuilder/tests/garble.test.ts | 21 +++++++
 2 files changed, 84 insertions(+)
 create mode 100644 frontend/localebuilder/garble.ts
 create mode 100644 frontend/localebuilder/tests/garble.test.ts

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
new file mode 100644
index 000000000..d6fbd45af
--- /dev/null
+++ b/frontend/localebuilder/garble.ts
@@ -0,0 +1,63 @@
+/**
+ * A function that takes a fragment of English text and garbles
+ * it in some way.
+ */
+export type Garbler = (text: string) => string;
+
+/**
+ * Take the raw string from a message catalog and "garble" it into
+ * gobbledygook.
+ */
+export function garbleMessage(garbler: Garbler, source: string): string {
+  const mg = new MessageGarbler(source, garbler);
+  return mg.garble();
+}
+
+class MessageGarbler {
+  private parts: string[] = [];
+
+  constructor(
+    readonly source: string,
+    readonly garbler: Garbler,
+  ) {}
+
+  garble(): string {
+    this.processEnglish(0);
+    return this.parts.join('');
+  }
+
+  private processVariable(i: number): number {
+    while (i < this.source.length) {
+      const ch = this.source[i];
+
+      if (ch === '}') {
+        this.parts.push(ch);
+        return i + 1;
+      } else {
+        this.parts.push(ch);
+      }
+
+      i++;
+    }
+    return i;
+  }
+
+  private processEnglish(i: number) {
+    let start = i;
+
+    while (i < this.source.length) {
+      const ch = this.source[i];
+
+      if (ch === '{') {
+        const english = this.source.substring(start, i);
+        this.parts.push(this.garbler(english));
+        i = this.processVariable(i);
+        start = i;
+      }
+      i++;
+    }
+
+    const english = this.source.substring(start, i);
+    this.parts.push(this.garbler(english));
+  }
+};
diff --git a/frontend/localebuilder/tests/garble.test.ts b/frontend/localebuilder/tests/garble.test.ts
new file mode 100644
index 000000000..221dd2476
--- /dev/null
+++ b/frontend/localebuilder/tests/garble.test.ts
@@ -0,0 +1,21 @@
+import { garbleMessage, Garbler } from "../garble";
+
+const trivialGarble: Garbler = (text) => {
+  return text.split(' ').map(word =>  word ? 'X' : '').join(' ');
+};
+
+describe("garbleMessage()", () => {
+  const garble = garbleMessage.bind(null, trivialGarble);
+
+  it('works with simple strings', () => {
+    expect(garble("Hello world")).toBe("X X");
+  });
+
+  it("Doesn't garble tags", () => {
+    expect(garble("<0>Hello world</0> <1/>")).toBe("<0>X X</0> <1/>");
+  });
+
+  it("Doesn't garble variables", () => {
+    expect(garble("Hello {firstName} how goes")).toBe("X {firstName} X X");
+  });
+});

From 555d66516103796e3d626b41ec37ce0cff90ca2e Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 12:00:41 -0400
Subject: [PATCH 02/11] Add stuff

---
 frontend/localebuilder/garble.ts | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index d6fbd45af..a44e7f1c5 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -26,11 +26,11 @@ class MessageGarbler {
     return this.parts.join('');
   }
 
-  private processVariable(i: number): number {
+  private chompUntil(i: number, char: string): number {
     while (i < this.source.length) {
       const ch = this.source[i];
 
-      if (ch === '}') {
+      if (ch === char) {
         this.parts.push(ch);
         return i + 1;
       } else {
@@ -42,22 +42,37 @@ class MessageGarbler {
     return i;
   }
 
+  private processVariable(i: number): number {
+    return this.chompUntil(i, '}');
+  }
+
+  private processTag(i: number): number {
+    return this.chompUntil(i, '>');
+  }
+
   private processEnglish(i: number) {
     let start = i;
 
+    const pushToParts = () => {
+      const english = this.source.substring(start, i);
+      this.parts.push(this.garbler(english));
+    };
+
     while (i < this.source.length) {
       const ch = this.source[i];
 
       if (ch === '{') {
-        const english = this.source.substring(start, i);
-        this.parts.push(this.garbler(english));
+        pushToParts();
         i = this.processVariable(i);
         start = i;
+      } else if (ch === '<') {
+        pushToParts();
+        i = this.processTag(i);
+        start = i;
       }
       i++;
     }
 
-    const english = this.source.substring(start, i);
-    this.parts.push(this.garbler(english));
+    pushToParts();
   }
 };

From 52dc5a2c177474648053d38c7c7e54d05eba6bb7 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 12:06:20 -0400
Subject: [PATCH 03/11] rename stuff

---
 frontend/localebuilder/garble.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index a44e7f1c5..e7b20ee61 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -53,7 +53,7 @@ class MessageGarbler {
   private processEnglish(i: number) {
     let start = i;
 
-    const pushToParts = () => {
+    const pushGarbledEnglish = () => {
       const english = this.source.substring(start, i);
       this.parts.push(this.garbler(english));
     };
@@ -62,17 +62,17 @@ class MessageGarbler {
       const ch = this.source[i];
 
       if (ch === '{') {
-        pushToParts();
+        pushGarbledEnglish();
         i = this.processVariable(i);
         start = i;
       } else if (ch === '<') {
-        pushToParts();
+        pushGarbledEnglish();
         i = this.processTag(i);
         start = i;
       }
       i++;
     }
 
-    pushToParts();
+    pushGarbledEnglish();
   }
 };

From 1a8a80aaea1bfc5e8893089beb50f341e5101ae5 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 12:26:15 -0400
Subject: [PATCH 04/11] more

---
 frontend/localebuilder/garble.ts | 110 +++++++++++++++++--------------
 1 file changed, 60 insertions(+), 50 deletions(-)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index e7b20ee61..055f59597 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -9,70 +9,80 @@ export type Garbler = (text: string) => string;
  * gobbledygook.
  */
 export function garbleMessage(garbler: Garbler, source: string): string {
-  const mg = new MessageGarbler(source, garbler);
-  return mg.garble();
+  const state = handleEnglish({
+    garbler,
+    source,
+    parts: [],
+    i: 0,
+  });
+  return state.parts.join("");
 }
 
-class MessageGarbler {
-  private parts: string[] = [];
+type GarblerState = {
+  parts: string[];
+  source: string;
+  i: number;
+  garbler: Garbler;
+};
 
-  constructor(
-    readonly source: string,
-    readonly garbler: Garbler,
-  ) {}
+type StateHandler = (s: GarblerState) => GarblerState;
 
-  garble(): string {
-    this.processEnglish(0);
-    return this.parts.join('');
-  }
+const handleEnglish: StateHandler = (s) => {
+  let { i, source, garbler, parts } = s;
+  let start = s.i;
 
-  private chompUntil(i: number, char: string): number {
-    while (i < this.source.length) {
-      const ch = this.source[i];
+  const pushGarbledEnglish = () => {
+    const english = s.source.substring(start, i);
+    parts = [...parts, garbler(english)];
+  };
 
-      if (ch === char) {
-        this.parts.push(ch);
-        return i + 1;
-      } else {
-        this.parts.push(ch);
-      }
+  while (i < source.length) {
+    const ch = source[i];
+    let newProcessor: StateHandler | null = null;
 
-      i++;
+    if (ch === "{") {
+      newProcessor = handleVariable;
+    } else if (ch === "<") {
+      newProcessor = handleTag;
     }
-    return i;
-  }
 
-  private processVariable(i: number): number {
-    return this.chompUntil(i, '}');
-  }
+    if (newProcessor) {
+      pushGarbledEnglish();
+      ({ i, parts } = newProcessor({ ...s, i, parts }));
+      start = i;
+    }
 
-  private processTag(i: number): number {
-    return this.chompUntil(i, '>');
+    i++;
   }
 
-  private processEnglish(i: number) {
-    let start = i;
+  pushGarbledEnglish();
+
+  return { ...s, i, parts };
+};
 
-    const pushGarbledEnglish = () => {
-      const english = this.source.substring(start, i);
-      this.parts.push(this.garbler(english));
+function chompUntil(s: GarblerState, char: string): GarblerState {
+  let { i, source } = s;
+  const newParts: string[] = [];
+  const finish = (i: number): GarblerState => {
+    return {
+      ...s,
+      i,
+      parts: [...s.parts, newParts.join("")],
     };
+  };
 
-    while (i < this.source.length) {
-      const ch = this.source[i];
-
-      if (ch === '{') {
-        pushGarbledEnglish();
-        i = this.processVariable(i);
-        start = i;
-      } else if (ch === '<') {
-        pushGarbledEnglish();
-        i = this.processTag(i);
-        start = i;
-      }
-      i++;
+  while (i < source.length) {
+    const ch = source[i];
+    newParts.push(ch);
+    if (ch === char) {
+      return finish(i + 1);
     }
-
-    pushGarbledEnglish();
+    i++;
   }
-};
+
+  return finish(i);
+}
+
+const handleVariable: StateHandler = (s) => chompUntil(s, "}");
+
+const handleTag: StateHandler = (s) => chompUntil(s, ">");

From 2b8cafd4cda431b0f17f4be6003b40986fb18790 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 14:29:18 -0400
Subject: [PATCH 05/11] minor refactoring

---
 frontend/localebuilder/garble.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index 055f59597..fc842ad4b 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -31,9 +31,9 @@ const handleEnglish: StateHandler = (s) => {
   let { i, source, garbler, parts } = s;
   let start = s.i;
 
-  const pushGarbledEnglish = () => {
+  const pushGarbledEnglish = (): string[] => {
     const english = s.source.substring(start, i);
-    parts = [...parts, garbler(english)];
+    return [...parts, garbler(english)];
   };
 
   while (i < source.length) {
@@ -47,7 +47,7 @@ const handleEnglish: StateHandler = (s) => {
     }
 
     if (newProcessor) {
-      pushGarbledEnglish();
+      parts = pushGarbledEnglish();
       ({ i, parts } = newProcessor({ ...s, i, parts }));
       start = i;
     }
@@ -55,7 +55,7 @@ const handleEnglish: StateHandler = (s) => {
     i++;
   }
 
-  pushGarbledEnglish();
+  parts = pushGarbledEnglish();
 
   return { ...s, i, parts };
 };

From 1e31c3a5c1d415e4806781152ed6ce9e779ec4c8 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 16:53:14 -0400
Subject: [PATCH 06/11] Factor out GarblerState.

---
 frontend/localebuilder/garble.ts | 130 ++++++++++++++++++-------------
 1 file changed, 74 insertions(+), 56 deletions(-)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index fc842ad4b..97c4858d2 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -5,84 +5,102 @@
 export type Garbler = (text: string) => string;
 
 /**
- * Take the raw string from a message catalog and "garble" it into
- * gobbledygook.
+ * Take the raw string from a message catalog and "garble" its English
+ * text into gobbledygook, while preserving all code.
  */
 export function garbleMessage(garbler: Garbler, source: string): string {
-  const state = handleEnglish({
-    garbler,
-    source,
-    parts: [],
-    i: 0,
-  });
-  return state.parts.join("");
+  const s = new GarblerState(garbler, source);
+  handleEnglish(s);
+  return s.value;
 }
 
-type GarblerState = {
-  parts: string[];
-  source: string;
-  i: number;
-  garbler: Garbler;
-};
+class GarblerState implements Iterator<string> {
+  private parts: string[] = [];
+  private i: number = 0;
+  private substringStartIndex: number = 0;
 
-type StateHandler = (s: GarblerState) => GarblerState;
+  constructor(readonly garbler: Garbler, readonly source: string) {}
 
-const handleEnglish: StateHandler = (s) => {
-  let { i, source, garbler, parts } = s;
-  let start = s.i;
+  private get hasSubstring() {
+    return this.i - this.substringStartIndex > 0;
+  }
 
-  const pushGarbledEnglish = (): string[] => {
-    const english = s.source.substring(start, i);
-    return [...parts, garbler(english)];
-  };
+  private get substring() {
+    return this.source.substring(this.substringStartIndex, this.i);
+  }
+
+  private push(value: string) {
+    this.parts.push(value);
+    this.substringStartIndex = this.i;
+  }
+
+  pushEnglish() {
+    this.hasSubstring && this.push(this.garbler(this.substring));
+  }
+
+  pushCode() {
+    this.hasSubstring && this.push(this.substring);
+  }
 
-  while (i < source.length) {
-    const ch = source[i];
-    let newProcessor: StateHandler | null = null;
+  backtrack() {
+    this.i--;
+  }
 
-    if (ch === "{") {
-      newProcessor = handleVariable;
-    } else if (ch === "<") {
-      newProcessor = handleTag;
+  next() {
+    if (this.i === this.source.length) {
+      return { value: "", done: true };
     }
+    const value = this.source[this.i];
+    this.i++;
+    return { value, done: false };
+  }
 
-    if (newProcessor) {
-      parts = pushGarbledEnglish();
-      ({ i, parts } = newProcessor({ ...s, i, parts }));
-      start = i;
+  pushCodeUntil(char: string) {
+    for (let ch of this) {
+      if (ch === char) {
+        this.pushCode();
+        this.next();
+        return;
+      }
     }
 
-    i++;
+    this.pushCode();
+  }
+
+  [Symbol.iterator]() {
+    return this;
   }
 
-  parts = pushGarbledEnglish();
+  get value() {
+    return this.parts.join("");
+  }
+}
 
-  return { ...s, i, parts };
+type StateHandler = (s: GarblerState) => void;
+
+type StateHandlerMap = {
+  [ch: string]: StateHandler | undefined;
 };
 
-function chompUntil(s: GarblerState, char: string): GarblerState {
-  let { i, source } = s;
-  const newParts: string[] = [];
-  const finish = (i: number): GarblerState => {
-    return {
-      ...s,
-      i,
-      parts: [...s.parts, newParts.join("")],
-    };
+const handleEnglish: StateHandler = (s) => {
+  const handlers: StateHandlerMap = {
+    "{": handleVariable,
+    "<": handleTag,
   };
 
-  while (i < source.length) {
-    const ch = source[i];
-    newParts.push(ch);
-    if (ch === char) {
-      return finish(i + 1);
+  for (let ch of s) {
+    let newHandler = handlers[ch];
+
+    if (newHandler) {
+      s.backtrack();
+      s.pushEnglish();
+      newHandler(s);
     }
-    i++;
   }
 
-  return finish(i);
-}
+  s.pushEnglish();
+};
 
-const handleVariable: StateHandler = (s) => chompUntil(s, "}");
+const handleVariable: StateHandler = (s) => s.pushCodeUntil("}");
 
-const handleTag: StateHandler = (s) => chompUntil(s, ">");
+const handleTag: StateHandler = (s) => s.pushCodeUntil(">");

From e353e7374634ecbbd392df77f2dd71e19ba63119 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 17:25:41 -0400
Subject: [PATCH 07/11] Support plurals.

---
 frontend/localebuilder/garble.ts            | 27 ++++++++++++++++++---
 frontend/localebuilder/tests/garble.test.ts | 22 ++++++++++++++---
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index 97c4858d2..2de216da6 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -55,9 +55,9 @@ class GarblerState implements Iterator<string> {
     return { value, done: false };
   }
 
-  pushCodeUntil(char: string) {
+  pushCodeUntil(chars: string) {
     for (let ch of this) {
-      if (ch === char) {
+      if (chars.indexOf(ch) !== -1) {
         this.pushCode();
         this.next();
         return;
@@ -82,13 +82,19 @@ type StateHandlerMap = {
   [ch: string]: StateHandler | undefined;
 };
 
-const handleEnglish: StateHandler = (s) => {
+const handleEnglish = (s: GarblerState, untilChar?: string) => {
   const handlers: StateHandlerMap = {
     "{": handleVariable,
     "<": handleTag,
   };
 
   for (let ch of s) {
+    if (ch === untilChar) {
+      s.backtrack();
+      s.pushEnglish();
+      return;
+    }
+
     let newHandler = handlers[ch];
 
     if (newHandler) {
@@ -101,6 +107,19 @@ const handleEnglish: StateHandler = (s) => {
   s.pushEnglish();
 };
 
-const handleVariable: StateHandler = (s) => s.pushCodeUntil("}");
+const handleVariable: StateHandler = (s) => {
+  s.next();
+  for (let ch of s) {
+    if (ch === '{') {
+      s.pushCode();
+      handleEnglish(s, '}');
+      s.next();
+    } else if (ch === '}') {
+      s.pushCode();
+      s.next();
+      return;
+    }
+  }
+};
 
 const handleTag: StateHandler = (s) => s.pushCodeUntil(">");
diff --git a/frontend/localebuilder/tests/garble.test.ts b/frontend/localebuilder/tests/garble.test.ts
index 221dd2476..b7c371f83 100644
--- a/frontend/localebuilder/tests/garble.test.ts
+++ b/frontend/localebuilder/tests/garble.test.ts
@@ -1,13 +1,16 @@
 import { garbleMessage, Garbler } from "../garble";
 
-const trivialGarble: Garbler = (text) => {
-  return text.split(' ').map(word =>  word ? 'X' : '').join(' ');
+const wordsToXs: Garbler = (text) => {
+  return text
+    .split(" ")
+    .map((word) => (word ? "X" : ""))
+    .join(" ");
 };
 
 describe("garbleMessage()", () => {
-  const garble = garbleMessage.bind(null, trivialGarble);
+  const garble = garbleMessage.bind(null, wordsToXs);
 
-  it('works with simple strings', () => {
+  it("works with simple strings", () => {
     expect(garble("Hello world")).toBe("X X");
   });
 
@@ -18,4 +21,15 @@ describe("garbleMessage()", () => {
   it("Doesn't garble variables", () => {
     expect(garble("Hello {firstName} how goes")).toBe("X {firstName} X X");
   });
+
+  it("Doesn't garble plurals", () => {
+    expect(
+      garble(
+        "Marshals scheduled {totalEvictions, plural, one {one eviction} " +
+          "other {# evictions}} across this portfolio."
+      )
+    ).toBe(
+      "X X {totalEvictions, plural, one {X X} other {X X}} X X X"
+    );
+  });
 });

From bb53fb95ce664f4f5c21543be2b7a1ddf979a09f Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 17:46:08 -0400
Subject: [PATCH 08/11] Add --garble to localebuilder.

---
 frontend/localebuilder/cli.ts | 56 +++++++++++++++++++++++++++++++----
 1 file changed, 50 insertions(+), 6 deletions(-)

diff --git a/frontend/localebuilder/cli.ts b/frontend/localebuilder/cli.ts
index d6970fd18..38c996cda 100644
--- a/frontend/localebuilder/cli.ts
+++ b/frontend/localebuilder/cli.ts
@@ -13,6 +13,8 @@ import {
 import { argvHasOption } from "../querybuilder/util";
 import { checkExtractedMessagesSync } from "./check-extracted-messages";
 import { assertNotUndefined } from "../lib/util/util";
+import PO from "pofile";
+import { garbleMessage, Garbler } from "./garble";
 
 const MY_DIR = __dirname;
 
@@ -57,19 +59,21 @@ const SPLIT_CHUNK_CONFIGS: MessageCatalogSplitterChunkConfig[] = [
   },
 ];
 
+function readTextFileSync(path: string): string {
+  return fs.readFileSync(path, {
+    encoding: "utf-8",
+  });
+}
+
 /**
  * Split up the message catalog for a single locale.
  */
 function processLocale(paths: MessageCatalogPaths, validate: boolean) {
   console.log(`Processing locale '${paths.locale}'.`);
 
-  const messagesJs = fs.readFileSync(paths.js, {
-    encoding: "utf-8",
-  });
+  const messagesJs = readTextFileSync(paths.js);
   const compiled = parseCompiledMessages(messagesJs);
-  const messagesPo = fs.readFileSync(paths.po, {
-    encoding: "utf-8",
-  });
+  const messagesPo = readTextFileSync(paths.po);
   var extracted = parseExtractedMessages(messagesPo);
   if (validate) {
     extracted.validateIdLengths(MAX_ID_LENGTH);
@@ -82,6 +86,40 @@ function processLocale(paths: MessageCatalogPaths, validate: boolean) {
   splitter.split();
 }
 
+const garbler: Garbler = (text) => {
+  return text.toUpperCase();
+};
+
+function garbleMessageCatalogs(
+  allPaths: MessageCatalogPaths[],
+  defaultPaths: MessageCatalogPaths
+) {
+  const defaultPo = PO.parse(readTextFileSync(defaultPaths.po));
+  const sources = new Map<string, string>();
+
+  for (let item of defaultPo.items) {
+    sources.set(item.msgid, garbleMessage(garbler, item.msgstr.join("")));
+  }
+
+  for (let paths of allPaths) {
+    if (paths === defaultPaths) continue;
+    const localePo = PO.parse(readTextFileSync(paths.po));
+
+    for (let item of localePo.items) {
+      const garbled = sources.get(item.msgid);
+      if (!garbled) {
+        throw new Error(
+          `${defaultPaths.locale} source not found for msgid "${item.msgid}"!`
+        );
+      }
+      item.msgstr = [garbled];
+    }
+
+    console.log(`Garbling ${paths.po}.`);
+    fs.writeFileSync(paths.po, localePo.toString(), { encoding: 'utf-8'});
+  }
+}
+
 /**
  * Main function to run the localebuilder CLI.
  */
@@ -90,6 +128,7 @@ export function run() {
     console.log(`usage: ${process.argv[1]} [OPTIONS]`);
     console.log(`options:\n`);
     console.log("  --check         Ensure PO files are up to date");
+    console.log("  --garble        Enact gobbledygook translation");
     console.log("  -h / --help     Show this help");
     console.log("  -v / --version  Show the version number");
     process.exit(0);
@@ -107,6 +146,11 @@ export function run() {
     process.exit(0);
   }
 
+  if (argvHasOption("--garble")) {
+    garbleMessageCatalogs(allPaths, defaultPath);
+    process.exit(0);
+  }
+
   let validate = true;
   for (let paths of allPaths) {
     processLocale(paths, validate);

From d3051fb2b1b6a629dba495349dc0aef1b91670a6 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 17:52:34 -0400
Subject: [PATCH 09/11] Fix bug.

---
 frontend/localebuilder/garble.ts            | 1 +
 frontend/localebuilder/tests/garble.test.ts | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index 2de216da6..b1e5beed6 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -101,6 +101,7 @@ const handleEnglish = (s: GarblerState, untilChar?: string) => {
       s.backtrack();
       s.pushEnglish();
       newHandler(s);
+      s.backtrack();
     }
   }
 
diff --git a/frontend/localebuilder/tests/garble.test.ts b/frontend/localebuilder/tests/garble.test.ts
index b7c371f83..f6beff995 100644
--- a/frontend/localebuilder/tests/garble.test.ts
+++ b/frontend/localebuilder/tests/garble.test.ts
@@ -22,6 +22,10 @@ describe("garbleMessage()", () => {
     expect(garble("Hello {firstName} how goes")).toBe("X {firstName} X X");
   });
 
+  it("Doesn't garble variables in tags", () => {
+    expect(garble("Hello <0>{firstName}</0> how goes")).toBe("X <0>{firstName}</0> X X");
+  });
+
   it("Doesn't garble plurals", () => {
     expect(
       garble(

From fd87b44a923c00113fd2dfedac9e638d44dd4871 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 17:56:28 -0400
Subject: [PATCH 10/11] Better garbling.

---
 frontend/localebuilder/cli.ts | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/frontend/localebuilder/cli.ts b/frontend/localebuilder/cli.ts
index 38c996cda..6730d453d 100644
--- a/frontend/localebuilder/cli.ts
+++ b/frontend/localebuilder/cli.ts
@@ -86,13 +86,14 @@ function processLocale(paths: MessageCatalogPaths, validate: boolean) {
   splitter.split();
 }
 
-const garbler: Garbler = (text) => {
-  return text.toUpperCase();
+const defaultGarbler: Garbler = (text) => {
+  return text.replace(/[A-Za-z]/g, '?');
 };
 
 function garbleMessageCatalogs(
   allPaths: MessageCatalogPaths[],
-  defaultPaths: MessageCatalogPaths
+  defaultPaths: MessageCatalogPaths,
+  garbler: Garbler = defaultGarbler
 ) {
   const defaultPo = PO.parse(readTextFileSync(defaultPaths.po));
   const sources = new Map<string, string>();

From b337bc28ea16b606583d6870dd25aae210fa74e1 Mon Sep 17 00:00:00 2001
From: Atul Varma <varmaa@gmail.com>
Date: Tue, 19 May 2020 18:04:02 -0400
Subject: [PATCH 11/11] Run prettier.

---
 frontend/localebuilder/cli.ts               | 4 ++--
 frontend/localebuilder/garble.ts            | 6 +++---
 frontend/localebuilder/tests/garble.test.ts | 8 ++++----
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/frontend/localebuilder/cli.ts b/frontend/localebuilder/cli.ts
index 6730d453d..8a7777da0 100644
--- a/frontend/localebuilder/cli.ts
+++ b/frontend/localebuilder/cli.ts
@@ -87,7 +87,7 @@ function processLocale(paths: MessageCatalogPaths, validate: boolean) {
 }
 
 const defaultGarbler: Garbler = (text) => {
-  return text.replace(/[A-Za-z]/g, '?');
+  return text.replace(/[A-Za-z]/g, "?");
 };
 
 function garbleMessageCatalogs(
@@ -117,7 +117,7 @@ function garbleMessageCatalogs(
     }
 
     console.log(`Garbling ${paths.po}.`);
-    fs.writeFileSync(paths.po, localePo.toString(), { encoding: 'utf-8'});
+    fs.writeFileSync(paths.po, localePo.toString(), { encoding: "utf-8" });
   }
 }
 
diff --git a/frontend/localebuilder/garble.ts b/frontend/localebuilder/garble.ts
index b1e5beed6..8b31e9535 100644
--- a/frontend/localebuilder/garble.ts
+++ b/frontend/localebuilder/garble.ts
@@ -111,11 +111,11 @@ const handleEnglish = (s: GarblerState, untilChar?: string) => {
 const handleVariable: StateHandler = (s) => {
   s.next();
   for (let ch of s) {
-    if (ch === '{') {
+    if (ch === "{") {
       s.pushCode();
-      handleEnglish(s, '}');
+      handleEnglish(s, "}");
       s.next();
-    } else if (ch === '}') {
+    } else if (ch === "}") {
       s.pushCode();
       s.next();
       return;
diff --git a/frontend/localebuilder/tests/garble.test.ts b/frontend/localebuilder/tests/garble.test.ts
index f6beff995..de11e1a57 100644
--- a/frontend/localebuilder/tests/garble.test.ts
+++ b/frontend/localebuilder/tests/garble.test.ts
@@ -23,7 +23,9 @@ describe("garbleMessage()", () => {
   });
 
   it("Doesn't garble variables in tags", () => {
-    expect(garble("Hello <0>{firstName}</0> how goes")).toBe("X <0>{firstName}</0> X X");
+    expect(garble("Hello <0>{firstName}</0> how goes")).toBe(
+      "X <0>{firstName}</0> X X"
+    );
   });
 
   it("Doesn't garble plurals", () => {
@@ -32,8 +34,6 @@ describe("garbleMessage()", () => {
         "Marshals scheduled {totalEvictions, plural, one {one eviction} " +
           "other {# evictions}} across this portfolio."
       )
-    ).toBe(
-      "X X {totalEvictions, plural, one {X X} other {X X}} X X X"
-    );
+    ).toBe("X X {totalEvictions, plural, one {X X} other {X X}} X X X");
   });
 });