Missing keys issue in i18next with only one React Component - i18next

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");
}
}
}
});

Related

React Native Storybooks Component not Loading

I'm trying to load the default stories that come when you first install Storybook. Had some issues getting the server to start but I managed to fix it by adding port and host in the config. But even after getting it to run, clicking on any of the components doesn't update.
I'm expecting to see a Button.
And ideas? Here's the storybook index.js. I'm using Expo.
// if you use expo remove this line
// import { AppRegistry } from "react-native";
import {
getStorybookUI,
configure,
addDecorator,
} from "#storybook/react-native";
// import { withKnobs } from '#storybook/addon-knobs';
import "./rn-addons";
// enables knobs for all stories
// addDecorator(withKnobs);
// import stories
configure(() => {
require("./stories");
}, module);
const StorybookUIRoot = getStorybookUI({
host: "192.168.100.6", // replace this ip address with your local ip address
port: "7007",
asyncStorage: null,
});
// If you are using React Native vanilla and after installation you don't see your app name here, write it manually.
// If you use Expo you should remove this line.
// AppRegistry.registerComponent("%APP_NAME%", () => StorybookUIRoot);
export default StorybookUIRoot;
Also not sure if this is related but I've had to comment out addon-knobs in addons.js because it can't find it even though I have addon-knobs in my package.json:
import '#storybook/addon-actions/manager';
import '#storybook/addon-links/manager';
// import '#storybook/addon-knobs/manager';
I've tried replacing it with
register
like I've read on here but it still didn't work.
import '#storybook/addon-knobs/register';

How to use hooks in custom plugins

I am trying to write a custom plugin for our Docusaurus site. I am able to wire up the custom component, but I cannot use hooks like useState or useEffect. The page crashes saying I'm using an invalid React hook.
I know its possible to use hooks because I see other plugins doing it so I'm sure its a syntax problem somewhere.
Here's my code:
index.ts
import path from 'path'
module.exports = function () {
return {
name: 'docusaurus-theme-myorg-technology',
getThemePath() {
return path.resolve(__dirname, './theme')
}
};
};
theme/index.tsx
import React from 'react'
import {CustomTOC} from './CustomTOC'
const WrappedTOC = (props: any) => {
return (
<CustomTOC {...props} />
);
};
export default WrappedTOC;
theme/CustomTOC.tsx
import React, { useState } from 'react';
import TOC from '#theme-init/TOC';
export default function CustomTOC(props: any) {
//const [tags, setTags] = useState<any[]>([]); <-- if I comment this out the page crashes
return (
<>
<TOC {...props} />
Hello world
</>
);
}
"Invalid hooks call" link to a doc page, that you should read carefully.
Most likely: you are using a different version of React for your component lib that the one Docusaurus uses internally, and it leads to the React lib being used twice at runtime. Make sure the final project will only include one React version. You can for example use the exact same version that the one Docusaurus uses

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.

Error in rendering vue.js file using view instance new Vue()

I am working with vue.js, while rendering vue page i am getting below
error "Cannot GET /KForm"
Below are my code in main.js
import * as componentBase from "#app/app_start"
import Form from "#views/Form/Form.vue"
import KForm from "#views/KForm/KForm"
const NotFound = { template: '<p>Page not found</p>' }
const routes = {
'/': Form,
'/recap': Recap,
'/KForm': KForm
}
const app = new Vue({
...componentBase,
data: {
currentRoute: window.location.pathname
},
computed: {
ViewComponent() {
return routes[this.currentRoute] || NotFound
}
},
render: h => h(this.ViewComponent)
}).$mount("#app")
I'm not sure what your webpack config is, but shouldn't the KForm import be this:
import KForm from "#views/KForm/KForm.vue"
^^^^
currentRoute is set to window.location.pathname one time only when the component is instantiated. When you click a link (or navigate directly from the browser's address bar) to, say /KForm, the window location changes and the browser tries to fetch the webpage at that new address just like in a traditional non-SPA webpage. This will fail unless the server responds to that URL.
To prevent the browser from doing this, you'll have to intercept <a> clicks and use the history API to change the window location without reloading the page, then change currentRoute accordingly.
Or better yet, just use vue-router which does all this for you. See this for example server configurations for HTML5 history mode.

Aurelia: change navigation in app.js from view

I have an Aurelia project with navigation in app.html and app.js. The project includes a home page that has a different style to it, including navigation that is different than the non-home page views.
I would like to turn off navigation for the home view so I tried setting a variable (showMenu) to toggle the visibility. In fact, I am able to use jQuery to do this, but I wonder if there is an Aurelia way of doing it. If I set this.showMenu to true it shows the menu container, and false hides it. Like this for example:
app.html
<div class="container" if.bind="showMenu">
app.js
constructor(router){
this.router = router;
this.showMenu = true;
...other things
}
What I would like to do is set showMenu to false from home.js. I tried this (among 20 or so other attempts), but it does not work.
home.js
activate() {
this.showMenu = false;
}
Is there a way through $parent or some other means to hide the menu in app.html using a view model?
EDIT
This works but it feels a little like a hack.
home.js
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
#inject(Router)
export class Home {
constructor(router) {
this.router = router;
}
attached(){
$("#navbarMenu").hide();
this.router.refreshNavigation();
}
}
You should be able to use router to achieve that. Since this is required for one page only, you can have something like this assuming your route name is home (or you could use other properties of RouteConfig that you have set in configureRouter):
<div class="container" if.bind="router.currentInstruction.config.name !== 'home'">
I approach this problem by using separate shells. By default Aurelia will start your app with app.js (or ts). But you can change that default and also use the same command to redirect to a new shell after authentication.
In your main.ts (or .js) you will have a line to start your aurelia app:
aurelia.start().then(() => aurelia.setRoot());
This line is telling aurelia to start and to set the root view model for your app, when aurelia.setRoot() has no value given it defaults to app.ts (or .js).
So I create a landing for my app where I can display with the page and styles I wish completely separately from the main app, including a limited router and navigation.
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration()
if (environment.debug) {
aurelia.use.developmentLogging();
}
if (environment.testing) {
aurelia.use.plugin('aurelia-testing');
}
aurelia.start().then(() => aurelia.setRoot('authPage'));
}
authPage.ts is my usual app.ts with a router configuration but it will only have the authPage configured in it and perhaps one or two other welcome pages.
The authPage takes care of authentication and obtaining appropriate tokens. I use a 3rd party for authentication services so all I have on this page is a link. Either way after successful authentication is confirmed you now just want to redirect to an alternative aurelia shell.
#autoinject
export class AuthPage {
private app : Aurelia;
private router : Router;
constructor(router : Router, app: Aurelia) {
this.app = app;
this.router = router;
}
authenticate {
//some kind of authentication procedure...
if(authenticationSuccess) {
this.router.navigate('/', { replace: true, trigger: false});
this.router.reset();
this.router.("authenticatedApp");
}
}
The lines this.router.navigate('/', { replace: true, trigger: false}); and this.router.reset(); are provided to deal with issues mentioned here and also on SO here. The shell switch line this.router.("authenticatedApp"); doesn't work for me without the other two.
My authenticatedApp configures a full router and navigation menu for the user in just the same way as you would normally do with app.ts but now separated into its own shell.
Of course there is nothing to prevent someone linking straight to authenticatedApp but at this point there is no data displayed without an api call which all require an access token to be presented.
This is a useful link on building an Aurelia app with multiple shells for authentication.
The end result is a separated landing pages and application pages which can have different styles and different navigation.On logout you can do the same thing in reverse to reload the auth page.