How can i customize form error message from Yup/Vee-validate using Vue-i18n in Ionic VueJS - vue.js

I would like to use vue-i18n to translate my form error messages.
I've code a "lang.ts" file which builds my i18n instance like this :
import { createI18n } from 'vue-i18n';
import french from './i18n/fr-FR.json';
import english from './i18n/en-US.json';
// push translations to list
const messages = {
'en-US': french,
'fr-FR': english
}
// Create VueI18n instance with options
export const i18n = createI18n({
locale:"en-US",
messages
});
and I initialize all of this in my "main.ts"
...
const app = createApp(App).use(router);
router.isReady().then(() => {
Device.getLanguageCode().then(function (lang) {
i18n.global.locale = lang.value; // changing locale using Capacitor locale getter
app.use(i18n)
.use(IonicVue)
app.mount('#app');
});
});
I'm using a yup custom schema to validate my form, and that's where i mention my custom error message, the problem is that my "global" locale value isn't being used in my schema and it always translates to default language (en-US here...)
here's my schema
export const LoginFormContentSchema = yup
.object({
login: yup.object({
email: yup.string().email(i18n.global.t('wrongMail')).required(i18n.global.t('emptyMail')), // translating my error messages to current global locale
password: yup.string().required(i18n.global.t('emptyPassword')),
url: yup
.string()
.required(i18n.global.t("urlEmpty")).lowercase()
.matches(/^(https:\/\/)?[a-z0-9._-]+(\.fr)/),
}),
})
.required();
Since i've changed my locale before mounting the app, i should have fr-FR as locale everywhere, but i'm getting "en-US" outputs when the error message pops out even thought my device is in French and my global.locale is fr-FR.

Related

Missing keys issue in i18next with only one React Component

I am developing a react-project with multilingual (en and fr). Everything is working fine and I am able to translate my app from en to fr and vice versa. The translations are working when refreshing the page too.
I have one React Component that is unable to translate and is showing a missing key error.
i18next::translator: missingKey fr translation CampaignDetails.description CampaignDetails.description
Instead of translating the text for description, it is showing me CampaignDetails.description exactly
PROBLEM EXPLANATION
Go to this URL http://donatenow-9cc92.web.app/
Try changing the language at the rightside of the header. It will work.
There is a search field at the leftside in the header. Search for "mervice" and wait until the
charity shows up
You can see a card for the searched charity, click on the card anywhere but not on follow
button
The URL now looks like this https://donatenow-9cc92.web.app/mervice
Try changing the language now and it will work too.
Refresh this page https://donatenow-9cc92.web.app/mervice and you will see the translations
are still working
Now scroll this page https://donatenow-9cc92.web.app/mervice and at the bottom, you will see
some campaigns like 'Sadqah', 'Zakat' and 'Dollar a Day for Sadaqa'. Click on any campaign,
lets say you have clicked 'Sadqah'
Now the URL is something like this https://donatenow-9cc92.web.app/mervice/sadqah
Try changing the language from this route and it will work fine
The problem is if you click on this link https://donatenow-9cc92.web.app/mervice/sadqah
directly or refresh the page. The translation will not work and the console will give you
missing keys error
EXPLANATION TO POINT 11
1- I have a NotFound.js component in which I am checking if the URL has any charity name like mervice. For example, if the URL is like this domainName/mervice then I am extracting the mervice from the URL and doing a Axios call to fetch the charity and then navigating to the charity page
https://donatenow-9cc92.web.app/mervice (The translations are working no matter if the user has come directly to the page or He/She searched for the charity)
2- In the same function of NotFound.js in which I am fetching the charity, I am also fetching the campaign for the charity. I am checking if the URL is like this domaninName/mervice/campaign and then if the campaign is found for the charity (mervice in this case) then I am navigating to the campaign page.
3- https://donatenow-9cc92.web.app/mervice/sadqah (The translation will not work if you click on this link directly but translations will work if you do it manually like using the search field)
If the translation is working for this page https://donatenow-9cc92.web.app/mervice then why it is not working for this page https://donatenow-9cc92.web.app/mervice/sadqah
The component is the same, I am only navigating in this component based on the condition
Index.js
import React, { Suspense } from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import "./css/Auth.css";
import "./css/SearchOrganization.css";
import "./css/Profile.css";
import "./css/Donations.css";
import "./css/ReceiptModal.css";
import "./css/Recurring.css";
import { Provider } from "react-redux";
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import store from "./redux/store";
const root = ReactDOM.createRoot(document.getElementById("root"));
i18n
.use(initReactI18next)
.use(LanguageDetector)
.use(HttpApi)
.init({
debug: true,
supportedLngs: ["en", "fr"],
fallbackLng: "en",
detection: {
order: ["localStorage"],
caches: ["localStorage"],
},
backend: {
loadPath: "assets/locales/{{lng}}/translation.json",
},
interpolation: {
escapeValue: false,
},
});
root.render(
<Suspense fallback={null}>
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
</Suspense>
);
reportWebVitals();
NotFound.js
axios
.get(`${hostName}api/v1/search/organization?name=${lowercase}&type=shortName`)
.then((res) => {
if (res.data.data.length > 0) {
// setting states
setLoader(false);
setError("");
// local storage
localStorage.setItem("organization", JSON.stringify(res.data.data[0]));
// setting props
setOrganizationDetails(res.data.data[0]);
// setting organization path
const data = res.data.data[0];
const organizationName = data.shortName;
const cleanOrganizationName = organizationName.replace(
/[^a-zA-Z0-9]/g,
""
);
setOrganizationPath(cleanOrganizationName.toLowerCase()); // redirects to charity translation is working
// fetching campaign for the given organization
for (let i = 0; i < data.campaigns.length; i += 1) {
const word = data.campaigns[i].name.replace(/[^a-zA-Z0-9]/g, "");
const lowercase = word.toLowerCase();
if (secondString === lowercase) {
setCharityPath(lowercase); // redirects to campaign, translation not working
setCharityDetails(data.campaigns[i]);
localStorage.setItem("campaign", JSON.stringify(data.campaigns[i]));
break;
} else {
setCharityPath(null);
setCharityDetails(null);
localStorage.removeItem("campaign");
}
}
}
});

How to Load Custom Language in Monaco using VueJS/Webpack

I've created a custom language using this tool here. I don't know what to do to load it to my VueJS app. I tried the following and get no errors, but it also doesn't show seem to work, because in the Monarch tool thing I get blue text on known functions etc, but in my editor I don't. Other languages work as expected.
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const path = require('path');
const main = path.resolve(__dirname, './src/test/test.ts');
module.exports = {
configureWebpack: {
plugins: [
new MonacoWebpackPlugin({
languages: ['javascript', 'typescript', 'python', 'java', 'python', 'json', 'vb'],
customLanguages: [
{
label: 'test',
entry: main
}
]
})
]
}
...
I made my .ts file essentially export a conf property with all the variables or whatever that are used in the tokenizer. I also exported a language property. I'm not totally sure that is the right format.
My .ts file essentially looks like:
export const conf = {...}
export const language = {...}
I'm not totally sure what to do here. Docs are sparse for custom languages and nothing seems to be working other than I think I at least have the first part of defining the language working.
That Webpack plugin isn't actually needed.
Based on the custom language example, you can register the language at runtime via monaco.languages.setMonarchTokensProvider(). The second function argument is an instance of IMonarchLanguage, which matches the language spec in the example you linked.
<script setup lang="ts">
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
import { ref, onMounted } from 'vue'
/**
* `customLangMonarch` contains the language spec example from
* https://microsoft.github.io/monaco-editor/monarch.html
*/
// #ts-ignore
import customLangMonarch from '#/custom-lang-monarch'
monaco.languages.register({ id: 'custom' })
monaco.languages.setMonarchTokensProvider('custom', customLangMonarch)
const editor = ref()
onMounted(() => {
monaco.editor.create(editor.value, {
language: 'custom',
})
})
</script>
demo w/Vue CLI
demo w/Vite

How to integrate inertiaJS with quasar framework?

I would like to integrate intertiaJS into my Quasar app so that I can communicate with my Laravel backend. My problem now is that the general stuff is taken over by the Quasar CLI, which is good in principle, but in this case it takes away my entry point as described at https://inertiajs.com/client-side-setup:
import { createApp, h } from 'vue'
import { App, plugin } from '#inertiajs/inertia-vue3'
const el = document.getElementById('app')
createApp({
render: () => h(App, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => require(`./Pages/${name}`).default,
})
}).use(plugin).mount(el)
My thought is that I could use a boot file like the offered in Quasar (https://quasar.dev/quasar-cli/boot-files), but I have to admit that I don't have the right approach.
When I look at the app.js that is automatically generated, I see that nothing special happens in the rendering:
/**
* THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* You are probably looking on adding startup/initialization code.
* Use "quasar new boot <name>" and add it there.
* One boot file per concern. Then reference the file(s) in quasar.conf.js > boot:
* boot: ['file', ...] // do not add ".js" extension to it.
*
* Boot files are your "main.js"
**/
import Vue from 'vue'
import './import-quasar.js'
import App from 'app/src/App.vue'
import createStore from 'app/src/store/index'
import createRouter from 'app/src/router/index'
export default async function () {
// create store and router instances
const store = typeof createStore === 'function'
? await createStore({Vue})
: createStore
const router = typeof createRouter === 'function'
? await createRouter({Vue, store})
: createRouter
// make router instance available in store
store.$router = router
// Create the app instantiation Object.
// Here we inject the router, store to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = {
router,
store,
render: h => h(App)
}
app.el = '#q-app'
// expose the app, the router and the store.
// note we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
return {
app,
store,
router
}
}
I.e. in principle I should be able to link in without it causing any conflict situations. The question is, how would that look?
I have to link into the rendering afterwards and overwrite it as described in the code example. I would like to stay with the Quasar Cli, because it is very useful and the situation described here is the only exception.
p7
the boot files is the right place to inject and initialize your own dependencies or just configure some startup code for your application.
I have not had the opportunity to use the library you mention, but I detail a little how you could implement
create your boot file
import { plugin } from '#inertiajs/inertia-vue';
export default async({ app, Vue }) => {
Vue.use(plugin);
}
until there you have 50%. On the other hand, you cannot do a mixin to the main instance but you could do it for each page, however I recommend that you make a component part to which you add the data you need and make a mixin of the library you need
<template>
<div />
</template>
<script>
import { App } from '#inertiajs/inertia-vue';
export default {
mixins: [App],
props: ['initialPage', 'resolveComponent'],
}
</script>
In order to do this, modify according to how the library you use works.

Pass single-spa prop to i18n instantiation

is there a way to pass a single-spa prop to a vue i18n instance, assigning it to the messages i18n prop.
I18n constructor:
const i18n = new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE,
messages: {},
});
Vue single-spa instance:
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
el: '#account',
render() {
return (
<App
eventBus={this.$data.eventBus}
lang={this.$data.lang}
></App>
);
},
i18n,
},
});
Info: I dont have access to lang outside the render function.
Basically, I need the lang prop inside my i18n messages object. I tried already to return the lang and but it into the constructor, doesnt work.
I also tried to reinstantiate i18n/messages after lang is received, doesnt work either.
Any other ideas?
So I figured it out. It is only possible to alter the messages in your mounted vue instance with:
this.$i18n.setLocaleMessage = newLangObj

How to not put "use strict" everywhere

I'm trying to write some tests for a React app I've been working on, and I figured I would use Jest since it's mentioned in the React docs.
I'm using Webpack and so far I've installed jest-cli, babel-jest, and I included the following configuration in package.json:
"jest": {
"scriptPreprocessor": "./node_modules/babel-jest",
"unmockedModulePathPatterns": [
"./node_modules/react",
"./node_modules/react-dom"
],
}
So, I'm writing the tests for some file foo.js. This file includes some other module bar.js (i.e. const bar = require('./bar');). Unfortunately, when I run jest I get the following error:
SyntaxError: Block-scoped declarations (let, const, function, class) not yet
supported outside strict mode in file 'js/foo.js'.
So, after some research, I find out I have to include 'use strict'; at the top of foo-test.js. However, for some reason, I still get the same error unless I also include 'use strict'; at the top of foo.js.
So my question is: am I doing something wrong? If not, is there anyway for me to write my tests using Jest without having to write 'use strict'; at the top of all my source files?
It seems to test out basic ES2015 classes with jest, use strict is required, however to test React Components, 'use strict' isn't required. Here's an example
//CheckboxWithLabel.js
import React, {Component} from 'react';
class CheckboxWithLabel extends Component {
constructor(){
super(...arguments);
this.state = {
isChecked: false
};
this.onChange = this.onChange.bind(this);
}
onChange() {
this.setState({
isChecked: !this.state.isChecked
});
}
render() {
return (
<label>
<input type="checkbox"
checked={this.state.isChecked}
onChange={this.onChange} />
{this.state.isChecked ? this.props.labelOn : this.props.labelOff }
</label>
);
}
}
export default CheckboxWithLabel;
//CheckboxWithLabel_tests.js
jest.disableAutomock(); //use this instead of jest.autoMockOff()
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import CheckboxWithLabel from '../source/components/CheckboxWithlabel';
// const CheckboxWithLabel = require('../source/components/CheckboxWithLabel');
describe('CheckboxWithlabel', () => {
const shallowRenderer = TestUtils.createRenderer();
//render a checkbox
const checkbox = TestUtils.renderIntoDocument(
<CheckboxWithLabel labelOn="On" labelOff="Off" />
);
// shallowRenderer.render(<CheckboxWithLabel labelOn="On" labelOff="Off" />)
// const checkbox = shallowRenderer.getRenderOutput();
// it('defaults to unchecked and off label', () => {
// const inputField = checkbox.props.children[0];
// const textNode = checkbox.props.children[1];
// expect(inputField.props.checked).toBe(false);
// expect(textNode).toEqual('Off');
// })
var checkboxNode = ReactDOM.findDOMNode(checkbox);
// let checkboxElement = TestUtils.findRenderedDOMComponentWithTag(checkbox, 'input');
it('defaults to Off label', () => {
expect(checkboxNode.textContent).toEqual('Off');
// expect(checkboxElement.checked).toBe(false);
});
})
Edited: This is not required anymore
Notice the only caveat being that you have to explicitly add a auto_mock_off.js file that simply adds this line (it took me hours to figure this one out)
jest.autoMockOff();
More information can be found on this thread on github Github React Issue #932
That's it! the component testing works perfectly. I've also tried the same example with shallow rendering and it worked perfectly too! Hope this helps!