Repository: xiCO2k/laravel-vue-i18n
Pull request #21 (2022-03-24 14:13:30)

fix: Add failsafe if there is no `php` files or `json` files.


Patch:
diff --git a/src/index.ts b/src/index.ts
index a6e8326..8d4a4ff 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,6 +4,7 @@ import { LanguageInterface } from './interfaces/language'
 import { LanguageJsonFileInterface } from './interfaces/language-json-file'
 import { ReplacementsInterface } from './interfaces/replacements'
 import { choose } from './pluralization'
+import { avoidExceptionOnPromise, avoidException } from './utils/avoid-exceptions';
 
 const isServer = typeof window === 'undefined'
 
@@ -139,12 +140,13 @@ function setLanguage({ lang, messages }: LanguageInterface): string {
  */
 async function resolveLang(callable: Function, lang: string): Promise<LanguageJsonFileInterface> {
   const hasPhpTranslations = typeof process !== 'undefined' && process.env ? process.env.LARAVEL_VUE_I18N_HAS_PHP : false;
-  const data = callable(lang)
+
+  let data = avoidException(callable, lang);
 
   if (data instanceof Promise) {
     if (hasPhpTranslations) {
-      const phpLang = await callable(`php_${lang}`);
-      const jsonLang = await data;
+      const phpLang = await avoidExceptionOnPromise(callable(`php_${lang}`));
+      const jsonLang = await avoidExceptionOnPromise(data);
 
       return new Promise((resolve) => resolve({
         default: {
@@ -154,14 +156,16 @@ async function resolveLang(callable: Function, lang: string): Promise<LanguageJs
       }));
     }
 
-    return data;
+    return new Promise(async resolve => resolve({
+      default: await avoidExceptionOnPromise(data),
+    }));
   }
 
   if (hasPhpTranslations) {
     return new Promise((resolve) => resolve({
       default: {
         ...data,
-        ...(callable(`php_${lang}`)),
+        ...(avoidException(callable, `php_${lang}`)),
       }
     }));
   }
diff --git a/src/utils/avoid-exceptions.ts b/src/utils/avoid-exceptions.ts
new file mode 100644
index 0000000..12a2c4f
--- /dev/null
+++ b/src/utils/avoid-exceptions.ts
@@ -0,0 +1,15 @@
+export const avoidException = (callback: Function, param, defaults = {}) => {
+  try {
+    return callback(param);
+  } catch {
+    return defaults;
+  }
+}
+
+export const avoidExceptionOnPromise = async (promise, defaults = {}) => {
+  try {
+    return (await promise).default || defaults;
+  } catch (e) {
+    return defaults;
+  }
+}


Test patch:
diff --git a/test/fixtures/lang/es.json b/test/fixtures/lang/es.json
new file mode 100644
index 0000000..0dcf227
--- /dev/null
+++ b/test/fixtures/lang/es.json
@@ -0,0 +1,3 @@
+{
+    "Welcome!": "Bienvenido!"
+}
diff --git a/test/fixtures/lang/fr/auth.php b/test/fixtures/lang/fr/auth.php
new file mode 100644
index 0000000..52fdf5c
--- /dev/null
+++ b/test/fixtures/lang/fr/auth.php
@@ -0,0 +1,5 @@
+<?php
+
+return [
+    'failed'   => 'Ces identifiants ne correspondent pas à nos enregistrements.',
+];
diff --git a/test/setup.ts b/test/setup.ts
index d7d7904..36c3610 100644
--- a/test/setup.ts
+++ b/test/setup.ts
@@ -17,6 +17,21 @@ global.mountPlugin = async (template = '<div />', lang = 'pt') => {
   return wrapper;
 }
 
+global.mountPluginWithRequire = async (template = '<div />', lang = 'pt') => {
+  const wrapper = mount({ template }, {
+    global: {
+      plugins: [[i18nVue, {
+        lang,
+        resolve: (lang) => require(`./fixtures/lang/${lang}.json`),
+      }]]
+    }
+  });
+
+  await new Promise(resolve => setTimeout(resolve))
+
+  return wrapper;
+}
+
 global.mixLoader = () => {
   parseAll(__dirname + '/fixtures/lang/');
 
diff --git a/test/translate.test.ts b/test/translate.test.ts
index b3bd488..3fe4cfc 100644
--- a/test/translate.test.ts
+++ b/test/translate.test.ts
@@ -125,16 +125,7 @@ it('resolves translated data without Promise', async () => {
 });
 
 it('resolves translated data with require', async () => {
-  const wrapper = mount({ template: `<h1>{{ $t('Welcome!') }}</h1>` }, {
-    global: {
-      plugins: [[i18nVue, {
-        lang: 'pt',
-        resolve: (lang) => require(`./fixtures/lang/${lang}.json`),
-      }]]
-    }
-  });
-
-  await new Promise(resolve => setTimeout(resolve));
+  const wrapper = await global.mountPluginWithRequire(`<h1>{{ $t('Welcome!') }}</h1>`);
 
   expect(trans('Welcome!')).toBe('Bem-vindo!');
 });
@@ -145,3 +136,31 @@ it('resolves translated data from .php files', async () => {
 
   expect(wrapper.html()).toBe('<h1>As credenciais indicadas não coincidem com as registadas no sistema.</h1>')
 });
+
+it('resolves translated data with loader if there is no .php files for that lang', async () => {
+  global.mixLoader();
+  const wrapper = await global.mountPlugin(`<h1 v-text="$t('Welcome!')" />`, 'es');
+
+  expect(wrapper.html()).toBe('<h1>Bienvenido!</h1>')
+})
+
+it('resolves translated data with loader if there is no .php files for that lang with require', async () => {
+  global.mixLoader();
+  const wrapper = await global.mountPluginWithRequire(`<h1 v-text="$t('Welcome!')" />`, 'es');
+
+  expect(wrapper.html()).toBe('<h1>Bienvenido!</h1>')
+});
+
+it('resolves translated data with loader if there is only .php files for that lang', async () => {
+  global.mixLoader();
+  const wrapper = await global.mountPlugin(`<h1 v-text="$t('auth.failed')" />`, 'fr');
+
+  expect(wrapper.html()).toBe('<h1>Ces identifiants ne correspondent pas à nos enregistrements.</h1>')
+})
+
+it('resolves translated data with loader if there is only .php files for that lang with require', async () => {
+  global.mixLoader();
+  const wrapper = await global.mountPluginWithRequire(`<h1 v-text="$t('auth.failed')" />`, 'fr');
+
+  expect(wrapper.html()).toBe('<h1>Ces identifiants ne correspondent pas à nos enregistrements.</h1>')
+})