From 0e0b8fc8f159b9e72cb8c9cc622ecc813031c996 Mon Sep 17 00:00:00 2001
From: Laurin Weger <Laurin-W@users.noreply.github.com>
Date: Sun, 7 Jul 2024 13:23:52 +0200
Subject: [PATCH] - i18n emojis - `{@html` for some translations - replace
 remaining in-code texts

---
 ng-app/src/App.svelte                   |   3 +
 ng-app/src/lib/Install.svelte           |   4 +-
 ng-app/src/lib/Login.svelte             | 124 ++++++++++++------------
 ng-app/src/lib/NoWallet.svelte          |   4 +-
 ng-app/src/locales/i18n-init.ts         |   9 +-
 ng-app/src/routes/User.svelte           |  16 +--
 ng-app/src/routes/UserRegistered.svelte |  18 ++--
 ng-app/src/routes/WalletInfo.svelte     |  51 +++++-----
 ng-app/src/wallet_emojis.ts             |  64 +++++++++++-
 9 files changed, 187 insertions(+), 106 deletions(-)

diff --git a/ng-app/src/App.svelte b/ng-app/src/App.svelte
index 258fde6..a5fe28c 100644
--- a/ng-app/src/App.svelte
+++ b/ng-app/src/App.svelte
@@ -269,6 +269,9 @@
     unsubscribe();
     if (unsub_main_close) unsub_main_close();
   });
+
+  // import { to_debug } from "./wallet_emojis";
+  // to_debug();
 </script>
 
 <!-- <p>
diff --git a/ng-app/src/lib/Install.svelte b/ng-app/src/lib/Install.svelte
index 19ceb72..0e5a81b 100644
--- a/ng-app/src/lib/Install.svelte
+++ b/ng-app/src/lib/Install.svelte
@@ -43,11 +43,11 @@
   </div>
 
   <p class="max-w-sm">
-    {$t("pages.install.app_availability")}
+    {@html $t("pages.install.app_availability")}
   </p>
   {#if display_has_wallets_warning}
     <Alert color="yellow" class="mt-5 block">
-      {$t("pages.install.has_wallet_warning")}
+      {@html $t("pages.install.has_wallet_warning")}
     </Alert>
   {/if}
   <div class="row mt-5">
diff --git a/ng-app/src/lib/Login.svelte b/ng-app/src/lib/Login.svelte
index 4315cb9..5ca2f53 100644
--- a/ng-app/src/lib/Login.svelte
+++ b/ng-app/src/lib/Login.svelte
@@ -16,9 +16,11 @@
 
 <script lang="ts">
   import { Alert, Toggle, Button } from "flowbite-svelte";
-  import { onMount, createEventDispatcher, tick } from "svelte";
+  import { onMount, createEventDispatcher } from "svelte";
+  import { t } from "svelte-i18n";
   import ng from "../api";
-  import { emoji_cat, emojis, load_svg } from "../wallet_emojis";
+  import { emoji_cat, emojis, load_svg, type Emoji } from "../wallet_emojis";
+
   import {
     PuzzlePiece,
     XCircle,
@@ -89,7 +91,7 @@
     scrollToTop();
   }
 
-  let emojis2 = [];
+  let emojis2: Emoji[][] = [];
 
   let shuffle;
 
@@ -333,72 +335,54 @@
   {#if step == "load"}
     <div class="flex flex-col justify-center p-4 pt-6">
       <h2 class="pb-5 text-xl self-start">
-        How to open your wallet? You have 2 options:
+        {$t("pages.login.heading")}
       </h2>
-      <h3 class="pb-2 text-lg self-start">With your Pazzle</h3>
+      <h3 class="pb-2 text-lg self-start">{$t("pages.login.with_pazzle")}</h3>
       <ul class="mb-8 ml-3 space-y-4 text-justify text-sm list-decimal">
         <li>
-          For each one of the 9 categories of images, you will be presented with
-          the 15 possible image choices. The categories are shuffled at every
-          login. They will not always appear in the same order.
+          {$t("pages.login.pazzle_steps.1")}
         </li>
         <li>
-          At each category, only one of the 15 displayed choices is the correct
-          image that belongs to your pazzle. Find it and tap or click on that
-          one. The 15 images are shuffled too, they will not appear at the same
-          position at each login. On a computer, you can also use the tab key on
-          your keyboard to move to the desired item on the screen, then press
-          the space bar to select each one.
+          {$t("pages.login.pazzle_steps.2")}
         </li>
         <li>
-          Once you completed the last category, you will be presented with all
-          the images you have previously selected. Their order is displayed as
-          it was when you picked them. But this is not the correct order of the
-          images in your pazzle. You now have to order them correctly.
+          {$t("pages.login.pazzle_steps.3")}
         </li>
         <li>
-          You must remember which image should be the first one in your pazzle.
-          Find it on the screen and click or tap on it. It will be greyed out
-          and the number 1 will appear on top of it.
+          {$t("pages.login.pazzle_steps.4")}
         </li>
         <li>
-          Move on to the second image of your pazzle (that you memorized). Find
-          it on the screen and tap on it. Repeat this step until you reached the
-          last image.
+          {$t("pages.login.pazzle_steps.5")}
         </li>
         <li>
-          Finally, your PIN code will be asked. enter it by clicking or tapping
-          on the digits.
+          {$t("pages.login.pazzle_steps.6")}
         </li>
       </ul>
 
       <h3 class="pb-2 text-lg self-start">
-        With your 12 words Mnemonic (passphrase)
+        {$t("pages.login.with_mnemonic")}
       </h3>
       <ul class="mb-8 ml-3 space-y-4 text-justify text-sm list-decimal">
         <li>
-          Enter your twelve words mnemonic in the input field. The words must be
-          separated by spaces.
+          {$t("pages.login.mnemonic_steps.1")}
+        </li>
+        <li>
+          {$t("pages.login.mnemonic_steps.2")}
         </li>
-        <li>Enter the PIN code that you chose when you created your wallet.</li>
       </ul>
 
       <!-- Save wallet? -->
       {#if for_import}
         <div class="max-w-xl lg:px-8 mx-auto px-4 mb-2">
-          <span class="text-xl">Do you trust this device? </span> <br />
+          <span class="text-xl">{$t("pages.login.trust_device")} </span> <br />
           <p class="text-sm">
-            If you do, if this device is yours or is used by few trusted persons
-            of your family or workplace, and you would like to login again from
-            this device in the future, then you can save your wallet on this
-            device. To the contrary, if this device is public and shared by
-            strangers, do not save your wallet here. {#if !tauri_platform}By
-              selecting this option, you agree to saving some cookies on your
-              browser.{/if}<br />
+            {$t("pages.login.trust_device_description")}
+            {#if !tauri_platform}
+              {$t("pages.login.trust_device_allow_cookies")}{/if}<br />
           </p>
           <div class="flex justify-center items-center my-4">
             <Toggle class="" bind:checked={trusted}
-              >Yes, save my wallet on this device</Toggle
+              >{$t("pages.login.trust_device_yes")}</Toggle
             >
           </div>
         </div>
@@ -407,7 +391,7 @@
       <div class=" max-w-xl lg:px-8 mx-auto px-4 text-primary-700">
         <div class="flex flex-col justify-centerspace-x-12 mt-4 mb-4">
           {#if !loaded}
-            Loading pazzle...
+            {$t("pages.login.loading_pazzle")}...
             <svg
               class="animate-spin my-4 h-14 w-14 mx-auto"
               xmlns="http://www.w3.org/2000/svg"
@@ -438,7 +422,7 @@
                 tabindex="-1"
                 class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none  group-hover:text-gray-900 dark:group-hover:text-white"
               />
-              Open with Pazzle!
+              {$t("pages.login.open_with_pazzle")}
             </button>
           {/if}
           <button
@@ -447,7 +431,7 @@
             ><ArrowLeft
               tabindex="-1"
               class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none  group-hover:text-gray-900 dark:group-hover:text-white"
-            />Cancel login</button
+            />{$t("pages.login.login_cancel")}</button
           >
           <span
             on:click={start_with_mnemonic}
@@ -456,7 +440,7 @@
             tabindex="0"
             class="mt-1 text-lg px-5 py-2.5 text-center inline-flex items-center mb-2 underline cursor-pointer"
           >
-            Open with Mnemonic instead
+            {$t("pages.login.open_with_mnemonic")}
           </span>
         </div>
       </div>
@@ -479,7 +463,7 @@
             <label
               for="mnemonic-input"
               class="block mb-2 text-xl text-gray-900 dark:text-white"
-              >Enter your 12 words mnemonic</label
+              >{$t("pages.login.enter_mnemonic")}</label
             >
             <PasswordInput
               id="mnemonic-input"
@@ -497,17 +481,21 @@
                 ><CheckCircle
                   tabindex="-1"
                   class="w-8 h-8 mr-2 -ml-1 transition duration-75  group-hover:text-gray-900 dark:group-hover:text-white"
-                />Confirm</Button
+                />{$t("buttons.confirm")}</Button
               >
             </div>
           </form>
         {:else if step == "pazzle"}
           <p class="max-w-xl mx-auto lg:max-w-2xl">
             <span class="text-xl">
-              <!-- TODO: Internationalization-->
-              Select your emoji of category:<br />{emoji_cat[
-                shuffle.category_indices[pazzlePage]
-              ]}</span
+              {@html $t("pages.login.select_emoji", {
+                values: {
+                  category: $t(
+                    "emojis.category." +
+                      emoji_cat[shuffle.category_indices[pazzlePage]]
+                  ),
+                },
+              })}</span
             >
           </p>
           {#each [0, 1, 2, 3, 4] as row}
@@ -517,6 +505,7 @@
                   role="button"
                   tabindex="0"
                   class="w-full aspect-square emoji focus:outline-none focus:bg-gray-300"
+                  title={$t("emojis.codes." + emoji.code)}
                   on:click={() => select(row * 3 + i)}
                   on:keypress={() => select(row * 3 + i)}
                 >
@@ -527,7 +516,7 @@
           {/each}
         {:else if step == "order"}
           <p class="max-w-xl mx-auto lg:max-w-2xl mb-2">
-            <span class="text-xl">Select each image in the correct order</span>
+            <span class="text-xl">{$t("pages.login.order_emojis")}</span>
           </p>
           {#each [0, 1, 2] as row}
             <div class="columns-3 gap-0">
@@ -539,6 +528,10 @@
                     class="w-full aspect-square emoji focus:outline-none focus:bg-gray-300"
                     on:click={() => select_order(emoji)}
                     on:keypress={() => select_order(emoji)}
+                    title={$t(
+                      "emojis.codes." +
+                        emojis[emoji_cat[emoji.cat]][emoji.index].code
+                    )}
                   >
                     <svelte:component
                       this={emojis[emoji_cat[emoji.cat]][emoji.index].svg
@@ -548,6 +541,10 @@
                 {:else}
                   <div
                     class="w-full aspect-square opacity-25 select-none sel-emoji"
+                    title={$t(
+                      "emojis.codes." +
+                        emojis[emoji_cat[emoji.cat]][emoji.index].code
+                    )}
                   >
                     <svelte:component
                       this={emojis[emoji_cat[emoji.cat]][emoji.index].svg
@@ -565,7 +562,7 @@
           {/each}
         {:else if step == "pin"}
           <p class="items-center">
-            <span class="text-xl">Enter your PIN code</span>
+            <span class="text-xl">{$t("pages.login.enter_pin")}</span>
           </p>
           <!-- Chrome requires the columns-3 __flex__ to be set, or else it wraps the lines incorrectly.
                However, this leads to the width decreasing and the buttons come together in mobile view.
@@ -625,7 +622,7 @@
           ><XCircle
             tabindex="-1"
             class="w-8 h-8 mr-2 -ml-1 transition focus:outline-none duration-75 group-hover:text-gray-900 dark:group-hover:text-white"
-          />Cancel</button
+          />{$t("buttons.cancel")}</button
         >
         <button
           class="mt-1 ml-2 min-w-[141px] focus:ring-4 focus:ring-primary-100/50 rounded-lg sm:text-lg px-5 py-2.5 text-center select-none inline-flex items-center dark:focus:ring-primary-700/55"
@@ -633,15 +630,18 @@
           ><Backspace
             tabindex="-1"
             class="w-8 h-8 mr-2 -ml-1 transition focus:outline-none duration-75 group-hover:text-gray-900 dark:group-hover:text-white"
-          />{#if step === "mnemonic" || (step === "pazzle" && pazzlePage === 0)}Go
-            back{:else}Correct{/if}</button
-        >
+          />
+          {#if step === "mnemonic" || (step === "pazzle" && pazzlePage === 0)}
+            {$t("buttons.go_back")}
+          {:else}
+            {$t("buttons.correct")}
+          {/if}
+        </button>
       </div>
     </div>
   {:else if step == "opening"}
     <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700">
-      Opening your wallet...<br />
-      Please wait
+      {@html $t("pages.login.opening_wallet")}
       <svg
         class="animate-spin mt-10 h-14 w-14 mx-auto"
         xmlns="http://www.w3.org/2000/svg"
@@ -668,7 +668,7 @@
     {#if error}
       <div class=" max-w-6xl lg:px-8 mx-auto text-red-800">
         <div class="mt-auto max-w-6xl lg:px-8">
-          An error occurred !
+          {$t("errors.an_error_occurred")}
           <svg
             fill="none"
             class="animate-bounce mt-10 h-10 w-10 mx-auto"
@@ -685,7 +685,7 @@
             />
           </svg>
           <Alert color="red" class="mt-5">
-            {error}
+            {$t("errors." + error)}
           </Alert>
         </div>
         <div class="flex justify-between mt-auto gap-4">
@@ -695,7 +695,7 @@
             ><XCircle
               tabindex="-1"
               class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white"
-            />Cancel</button
+            />{$t("buttons.cancel")}</button
           >
           <button
             class="mt-10 ml-2 select-none text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-100/50 rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55"
@@ -705,13 +705,13 @@
               tabindex="-1"
               class="w-8 h-8 mr-2 -ml-1 transition duration-75 focus:outline-none group-hover:text-gray-900 dark:group-hover:text-white"
             />
-            Try again
+            {$t("buttons.try_again")}
           </button>
         </div>
       </div>
     {:else}
       <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-green-800">
-        Your wallet is opened! <br />Please wait while the app is loading...
+        {@html $t("pages.login.wallet_opened")}
         <svg
           class="my-10 h-16 w-16 mx-auto"
           fill="none"
diff --git a/ng-app/src/lib/NoWallet.svelte b/ng-app/src/lib/NoWallet.svelte
index 2cf35c6..91f30e5 100644
--- a/ng-app/src/lib/NoWallet.svelte
+++ b/ng-app/src/lib/NoWallet.svelte
@@ -30,7 +30,7 @@
     <h1 class="text-2xl mb-10">{$t("pages.no_wallet.welcome")}</h1>
 
     <p class="max-w-sm">
-      {$t("pages.no_wallet.description")}
+      {@html $t("pages.no_wallet.description")}
     </p>
     <div class="row mt-5">
       <a href="/wallet/create" use:link>
@@ -78,7 +78,7 @@
               d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"
             />
           </svg>
-          {$t("common.login")}
+          {$t("buttons.login")}
         </button>
       </a>
     </div>
diff --git a/ng-app/src/locales/i18n-init.ts b/ng-app/src/locales/i18n-init.ts
index a7fe459..86ab66f 100644
--- a/ng-app/src/locales/i18n-init.ts
+++ b/ng-app/src/locales/i18n-init.ts
@@ -1,8 +1,13 @@
 import { register, init, getLocaleFromNavigator } from "svelte-i18n";
 
 register("en", () => import("./en.json"));
-// register('de', () => import('./de.json'));
-// register('fr', () => import('./fr.json'));
+register("de", () => import("./de.json"));
+register("fr", () => import("./fr.json"));
+register("ru", () => import("./ru.json"));
+register("es", () => import("./es.json"));
+register("it", () => import("./it.json"));
+register("zh", () => import("./zh.json"));
+register("pt", () => import("./pt.json"));
 
 init({
   fallbackLocale: "en",
diff --git a/ng-app/src/routes/User.svelte b/ng-app/src/routes/User.svelte
index 45608f1..c6e5223 100644
--- a/ng-app/src/routes/User.svelte
+++ b/ng-app/src/routes/User.svelte
@@ -154,7 +154,7 @@
                   class="w-7 h-7 text-green-600 transition duration-75 focus:outline-none dark:text-green-400 "
                 />
                 <span class="ml-3 text-green-600 dark:text-green-400"
-                  >{$t("pages.user_panel.online")}</span
+                  >{$t("connectivity.online")}</span
                 >
               {:else}
                 <SignalSlash
@@ -162,7 +162,7 @@
                   class="w-7 h-7 text-red-600 transition duration-75 focus:outline-none dark:text-red-400 "
                 />
                 <span class="ml-3 text-red-600 dark:text-red-400"
-                  >{$t("pages.user_panel.offline")}</span
+                  >{$t("connectivity.offline")}</span
                 >
               {/if}
             </li>
@@ -178,7 +178,7 @@
                 tabindex="-1"
                 class="w-7 h-7 text-black transition duration-75 focus:outline-none dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
               />
-              <span class="ml-3">{$t("common.logout")}</span>
+              <span class="ml-3">{$t("buttons.logout")}</span>
             </li>
             <!-- <li
               tabindex="0"
@@ -206,7 +206,7 @@
               </svelte:fragment>
             </SidebarItem>
             <SidebarItem
-              label={$t("pages.wallet.title")}
+              label={$t("pages.wallet_info.title")}
               href="#/wallet"
               class="p-2"
             >
@@ -352,26 +352,26 @@
         </svg>
         {#if error == "AlreadyExists"}
           <p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
-            {$t("pages.user_panel.already_registered")}
+            {@html $t("errors.AlreadyExists")}
           </p>
           <a use:link href="/">
             <button
               tabindex="-1"
               class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
             >
-              {$t("common.login")}
+              {$t("buttons.login")}
             </button>
           </a>
         {:else}
           <p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
-            {$t("pages.user_panel.error", { values: { error } })}
+            {@html $t("errors.error_occured", { values: { error } })}
           </p>
           <a use:link href="/">
             <button
               tabindex="-1"
               class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
             >
-              {$t("common.back_to_homepage")}
+              {$t("buttons.back_to_homepage")}
             </button>
           </a>
         {/if}
diff --git a/ng-app/src/routes/UserRegistered.svelte b/ng-app/src/routes/UserRegistered.svelte
index 8d2aa96..3b8e0d4 100644
--- a/ng-app/src/routes/UserRegistered.svelte
+++ b/ng-app/src/routes/UserRegistered.svelte
@@ -66,26 +66,26 @@
         </svg>
         {#if error == "AlreadyExists"}
           <p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
-            {$t("pages.user_registered.already_exists")}
+            {@html $t("pages.user_registered.already_exists")}
           </p>
           <a use:link href="/">
             <button
               tabindex="-1"
               class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
             >
-              {$t("common.login")}
+              {$t("buttons.login")}
             </button>
           </a>
         {:else}
           <p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
-            {$t("pages.user_registered.error", { values: { error } })}
+            {@html $t("errors.error_occured", { values: { error } })}
           </p>
           <a use:link href="/">
             <button
               tabindex="-1"
               class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
             >
-              {$t("common.back_to_homepage")}
+              {$t("buttons.back_to_homepage")}
             </button>
           </a>
         {/if}
@@ -108,9 +108,13 @@
           />
         </svg>
         <p class="max-w-xl md:mx-auto lg:max-w-2xl">
-          {$t("pages.user_registered.success", {
-            values: { invitation_name: invitation?.V0?.name },
-          })}
+          {#if invitation?.V0?.name}
+            {$t("pages.user_registered.success_with_invitation", {
+              values: { invitation_name: invitation?.V0?.name },
+            })}
+          {:else}
+            {$t("pages.user_registered.success")}
+          {/if}
         </p>
       </div>
     {/if}
diff --git a/ng-app/src/routes/WalletInfo.svelte b/ng-app/src/routes/WalletInfo.svelte
index 980b91b..5f2553a 100644
--- a/ng-app/src/routes/WalletInfo.svelte
+++ b/ng-app/src/routes/WalletInfo.svelte
@@ -30,7 +30,7 @@
   } from "svelte-heros-v2";
   import { onMount, tick } from "svelte";
   import { Sidebar, SidebarGroup, SidebarWrapper } from "flowbite-svelte";
-
+  import { t } from "svelte-i18n";
   import { close_active_wallet, active_session, active_wallet } from "../store";
 
   import { default as ng } from "../api";
@@ -104,7 +104,7 @@
         >
           <SidebarGroup ulClass="space-y-2" role="menu">
             <li>
-              <h2 class="text-xl mb-6">Wallet Info</h2>
+              <h2 class="text-xl mb-6">{$t("pages.wallet_info.title")}</h2>
             </li>
 
             <!-- Go Back -->
@@ -138,7 +138,7 @@
                     class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
                   />
                 </div>
-                <span class="ml-3">Download Wallet File</span>
+                <span class="ml-3">{$t("pages.wallet_info.download")}</span>
               </li>
             {:else if download_error}
               <li
@@ -152,7 +152,9 @@
                   />
                 </div>
                 <span class="ml-3 text-left"
-                  >Download failed:<br /> {download_error}</span
+                  >{$t("pages.wallet_info.download_failed", {
+                    values: { error: download_error },
+                  })}</span
                 >
               </li>
             {:else if !wallet_file_ready}
@@ -166,7 +168,9 @@
                     class="w-7 h-7 text-blue-700  transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
                   />
                 </div>
-                <span class="ml-3 text-left">Download in progress...</span>
+                <span class="ml-3 text-left"
+                  >{$t("pages.wallet_info.download_in_progress")}</span
+                >
               </li>
             {:else if download_link === true}
               <li
@@ -174,8 +178,9 @@
                 class="flex p-2 text-sm text-left break-all font-normal text-blue-700 rounded-lg dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700"
               >
                 <span
-                  >You will find the file named "{wallet_file_ready}" <br />in
-                  your Downloads folder</span
+                  >{@html $t("pages.wallet_info.download_successful", {
+                    values: { wallet_file: wallet_file_ready },
+                  })}</span
                 >
               </li>
             {:else}
@@ -198,7 +203,7 @@
                         class="w-14 h-14  transition duration-75 dark:text-white  dark:group-hover:text-white"
                       />
                     </div>
-                    Click here to download the wallet file
+                    {$t("pages.wallet_info.download_file_button")}
                   </button>
                 </a>
               </li>
@@ -218,7 +223,7 @@
                   class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
                 />
               </div>
-              <span class="ml-3">Remove Wallet from Device</span>
+              <span class="ml-3">{$t("pages.wallet_info.remove_wallet")}</span>
             </li>
             <Modal
               autoclose
@@ -227,16 +232,16 @@
               title="Remove Wallet"
             >
               <p class="mt-4">
-                Are you sure you want to remove this wallet from your device?
+                {$t("pages.wallet_info.remove_confirm")}
               </p>
               <div class="mt-4 flex justify-end">
-                <button on:click={close_modal}> Cancel </button>
+                <button on:click={close_modal}>{$t("buttons.cancel")}</button>
 
                 <button
                   class="mr-2 bg-primary-700 text-white"
                   on:click={remove_wallet_confirmed}
                 >
-                  Remove
+                  {$t("buttons.remove")}
                 </button>
               </div>
             </Modal>
@@ -254,10 +259,13 @@
                     class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
                   />
                 </div>
-                <span class="ml-3">Wallet QR-Code</span>
+                <span class="ml-3">{$t("qr_code")}</span>
               </li>
-              <Modal autoclose outsideclose title="My Wallet QR-Code"
-                >Use this QR-Code to log in with your wallet on new devices.
+              <Modal
+                autoclose
+                outsideclose
+                title={$t("pages.wallet_info.qr_modal_title")}
+                >{@html $t("pages.wallet_info.qr_modal_description")}
               </Modal>
             {/if}
 
@@ -274,7 +282,7 @@
                     class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
                   />
                 </div>
-                <span class="ml-3">Copy Wallet Link</span>
+                <span class="ml-3">{$t("pages.login.copy_wallet_link")}</span>
               </li>
             {/if}
 
@@ -290,7 +298,7 @@
                   tabindex="-1"
                   class="w-7 h-7 text-black transition duration-75 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white"
                 />
-                <span class="ml-3">Save to Device for Future Logins</span>
+                <span class="ml-3">{$t("pages.login.keep_wallet")}</span>
               </li>
             {/if}
           </SidebarGroup>
@@ -316,27 +324,26 @@
         </svg>
         {#if error == "AlreadyExists"}
           <p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
-            The user is already registered with the selected broker.<br /> Try logging
-            in instead
+            {@html $t("errors.AlreadyExists")}
           </p>
           <a use:link href="/">
             <button
               tabindex="-1"
               class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
             >
-              Login
+              {$t("buttons.login")}
             </button>
           </a>
         {:else}
           <p class="max-w-xl md:mx-auto lg:max-w-2xl mb-5">
-            An error occurred:<br />{error}
+            {@html $t("errors.error_occurred", { values: { error } })}
           </p>
           <a use:link href="/">
             <button
               tabindex="-1"
               class="text-white bg-primary-700 hover:bg-primary-700/90 focus:ring-4 focus:ring-primary-700/50 font-medium rounded-lg text-lg px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-primary-700/55 mb-2"
             >
-              Go back to homepage
+              {$t("buttons.back_to_homepage")}
             </button>
           </a>
         {/if}
diff --git a/ng-app/src/wallet_emojis.ts b/ng-app/src/wallet_emojis.ts
index d9e2e19..81363c7 100644
--- a/ng-app/src/wallet_emojis.ts
+++ b/ng-app/src/wallet_emojis.ts
@@ -7,6 +7,13 @@
 // notice may not be copied, modified, or distributed except
 // according to those terms.
 
+export type Emoji = {
+  hexcode: string;
+  shortcode: string;
+  code: string;
+  svg?: any;
+};
+
 let face = [
   {
     hexcode: "1f600",
@@ -1709,7 +1716,7 @@ export async function load_svg() {
   });
 }
 
-export const emojis = {
+export const emojis: Record<string, Emoji[]> = {
   face,
   face_unwell,
   face_costume,
@@ -1765,3 +1772,58 @@ export function emojis_from_pazzle_ids(pazzle: number[]) {
     return { cat: cat_name, ...emojis[cat_name][idx] };
   });
 }
+
+/*
+import en_annotations from "./locales/en_annotations.json";
+import de_annotations from "./locales/de_annotations.json";
+import fr_annotations from "./locales/fr_annotations.json";
+import ru_annotations from "./locales/ru_annotations.json";
+import es_annotations from "./locales/es_annotations.json";
+import it_annotations from "./locales/it_annotations.json";
+import zh_annotations from "./locales/zh_annotations.json";
+import pt_annotations from "./locales/pt_annotations.json";
+
+function i18n_for_emoji(annotations, emoji: Emoji) {
+  const char = String.fromCodePoint(Number.parseInt(emoji.hexcode, 16));
+  return annotations.annotations.annotations[char]?.tts[0];
+}
+
+function match_codes(annotations, emojis: Record<string, Emoji[]>) {
+  const not_found = [];
+  const map: Record<string, string> = {};
+  for (const cat in emojis) {
+    for (const emoji of emojis[cat]) {
+      const name = i18n_for_emoji(annotations, emoji);
+      if (!name) {
+        not_found.push(emoji);
+      } else {
+        map[emoji.code] = name;
+      }
+    }
+  }
+  if (not_found.length > 0) {
+    console.log("Not found: ", not_found);
+  }
+  return map;
+}
+
+export function to_debug() {
+  const en = match_codes(en_annotations, emojis);
+  const de = match_codes(de_annotations, emojis);
+  const fr = match_codes(fr_annotations, emojis);
+  const ru = match_codes(ru_annotations, emojis);
+  const es = match_codes(es_annotations, emojis);
+  const it = match_codes(it_annotations, emojis);
+  const zh = match_codes(zh_annotations, emojis);
+  const pt = match_codes(pt_annotations, emojis);
+
+  console.debug("en", JSON.stringify(en));
+  console.debug("de", JSON.stringify(de));
+  console.debug("fr", JSON.stringify(fr));
+  console.debug("ru", JSON.stringify(ru));
+  console.debug("es", JSON.stringify(es));
+  console.debug("it", JSON.stringify(it));
+  console.debug("zh", JSON.stringify(zh));
+  console.debug("pt", JSON.stringify(pt));
+}
+*/