Is it possible to get i18n translation source file from firebase? - firebase

Is it possible to modify vue-i18n so translation single JSON file with 2 different language inside and which come form Firebase will render text dynamically to vue template
I used mounted function with axios to get data form firebase. But I have no clue how i18n should treat this as a source file for a translation.
Basically I put my two translation source files from locales folder to the one United.json and I uploaded this to firebase, now I want to load this back to my app form there (firebase) but I see no solution there how to set locale so that it will render and translate all the {{ $t(example.locale) }} and I should be able to be bind it as this v-bind:src="$t('products.'+ index +'.options.'+ i +'.image')"
//main.js
import Vue from 'vue'
import i18n from './i18n'
import axios from 'axios'
Vue.config.productionTip = false
router.beforeEach((to, from, next) => {
let language = to.params.lang;
if (!language) {
language = 'ee'
}
i18n.locale = language
next()
});
new Vue({
router,
i18n,
render: h => h(App),
data() {
return{
localekk : ''
}
}
,
methods: {
setLocale(lang) {
Vue.$i18n.locale = lang;
}
},
mounted() {
axios.get('https://example.firebaseio.com/locales.json')
.then(response => {
this.locale = response.data;
console.log(response)
})
.catch(error => console.log(error))
}
}).$mount('#app')
//i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
function loadLocaleMessages () {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i) // may by to change './locales' but again - how?
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key)
}
})
return messages
}
export default new VueI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'ee',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'ee',
messages: loadLocaleMessages(),
silentTranslationWarn: true,
})

Related

How to connect RTKQ with CreateSlice? No date in store from slice

Main problem: no datÄ™ in store from slice.
Description: I want to create shop. I used to RTKQ to fetch date from endpoint and slice to create shop cart (increase, decrease, remove, add etc).
Problem is that I can connect it to one working system. I read a lot that it is not recommended and you try to prevent from that thing. But I also read that this is possible.
I read the solution that I can put as extraReducers to slice, but I want know how to connect it by store.
import { configureStore } from "#reduxjs/toolkit";
import { setupListeners } from "#reduxjs/toolkit/dist/query";
import { DummyShopApi } from "./reducers/itemSlice";
import { cartSlice } from "./reducers/cartSlice";
import {reducerPath} from "./reducers/itemSlice";
import { combineReducers } from 'redux'
export const rootReducer = combineReducers({
card: cartSlice,
[DummyShopApi.reducerPath]: DummyShopApi.reducer
});
export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(DummyShopApi.middleware),
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
setupListeners(store.dispatch);
it's common question and you could find the solution in documentation, but...))) I'll explain
When you have some query, what looks like:
export const pizzaApi = api.injectEndpoints({
endpoints: (builder) => ({
getPizzas: builder.Query<pizzasResponse, any>({
query: ({ sortBy }) => {
const sortBy1 = `sortBy=${sortBy ? sortBy : 'price'}`;
return {
url: `api?${sortBy1}`
};
}
})
})
});
export const { useGetPizzasQuery } = pizzaApi;
You get a 'queryHook' called useGetPizzasQuery. To add fetched pizzas from it to state (pizzaSlice), you should define extra reducer in your's slice:
extraReducers: (builder) => {
builder
.addMatcher(pizzaApi.endpoints.getPizzas.matchFulfilled, (state, action) => {
state.pizzas = action.payload.pizzas.content;
});
}

Vue3 testing ElementPlus controls with vitest

I am running Vue3 with vite and can't write any tests for components that use the ElementPlus library. Something else needs to be injected apparently but I don't know how to do that.
I have the following dateControl.test.js:
import { describe, expect, test } from 'vitest';
import { ref } from 'vue';
import DateCtrl from '#/components/DateCtrl.vue';
import { mount } from "#vue/test-utils";
import ElementPlus from "element-plus";
describe("DateCtrl.vue", () => {
const messages = {
"en-US" : {
strings: {
placeholder: 'a',
label: 'b'
}
}
};
const locale = "en-US";
const data = ref ({
date: ''
});
test ("Arrange DateCtrl", async () => {
const component = mount(DateCtrl, {
props: {
vModel: data.value.date,
modelValue: data.value.date,
labelLoc: "label",
className: "w1x5",
placeholderLoc: "date"
},
global: {
plugins: [ElementPlus],
mocks: {
$t: (msg) => {
const params = msg.split('.');
return messages[locale][params[0]][params[1]];
}
}
}
});
//fails on previous lines.
expect(typeof component !== "undefined", "component created").toBeTruthy();
let h3Text = component.findAll('h3')[0].element.innerHTML;
expect(component.findAll('.form').length === 1, "form element rendered").toBeTruthy();
expect(h3Text === "d", "locale strings correct").toBeTruthy();
});
});
It doesn't even get to the "expect" tests, fails with message:
Error: Cannot find module 'C:\source\mySite\node_modules\dayjs\plugin\customParseFormat'
imported from
C:\source\mySite\node_modules\element-plus\es\components\date-picker\src\date-picker.mjs
Did you mean to import dayjs/plugin/customParseFormat.js?
This bit seems to indicate that node expects you to use .js extension and element is not doing that.
Error: Cannot find module 'C:\source\mySite\node_modules\dayjs\plugin\customParseFormat'
imported from
C:\source\mySite\node_modules\element-plus\es\components\date-picker\src\date-picker.mjs
Did you mean to import dayjs/plugin/customParseFormat.js?
I'm guessing this is because you may be running an older node version. Element requires at least node v16.
I have this problem too.
It seems that this problem is already solved in this pull request - https://github.com/element-plus/element-plus/pull/6811

Nextjs dynamic routes with next-i18next build error

I have an edit page that will be rendered with an id parameter and it works fine when application is running but while building the nextjs app I get this error
[Error: ENOENT: no such file or directory, rename 'C:\Users\Ahsan Nisar\Documents\GitHub\customer-portal\frontend.next\export\en\companies\edit[id].html' -> 'C:\Users\Ahsan Nisar\Documents\GitHub\customer-portal\frontend.next\server\pages\en\companies\edit[id].html']
the full error
I am not sure what this error is related to or what mistake am I making in my code that this error is occuring during build time.
Here is the code of my page
import { WithAuthorization } from 'common/roq-hocs';
import { MainLayout } from 'layouts';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import React, { FunctionComponent } from 'react';
import { CompaniesEditView } from 'views/companies-edit';
const CompanyCreatePage: FunctionComponent = () => {
const { t } = useTranslation('companiesEdit');
return (
<MainLayout title={t('title')}>
<WithAuthorization
permissionKey="companies.update"
failComponent={
<div className="mt-16 text-2xl text-center text-gray-600">
<span>{t('noView')}</span>
</div>
}
>
<CompaniesEditView />
</WithAuthorization>
</MainLayout>
);
};
export const getStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['common', 'companiesEdit'])),
},
});
export const getStaticPaths = () => ({
paths: ['/companies/edit/[id]'],
fallback: true,
});
export default CompanyCreatePage;
I think that the problem might be that you are not returning the expected paths model in getStaticPaths function.
Minimal example of this page:
import { GetStaticPaths, GetStaticProps } from 'next';
import { useRouter } from 'next/router';
const CompanyCreatePage = () => {
const router = useRouter();
const { id } = router.query;
return (
<div>
<h1>Company Create Page Content for id: {id}</h1>
</div>
);
};
export const getStaticPaths: GetStaticPaths = async () => {
// Get all possible 'id' values via API, file, etc.
const ids = ['1', '2', '3', '4', '5']; // Example
const paths = ids.map(id => ({
params: { id },
}));
return { paths, fallback: false };
};
export const getStaticProps: GetStaticProps = async context => {
return { props: {} };
};
export default CompanyCreatePage;
Then, navigating to the page /users/edit/3/ returns the following content
Take into account that the fallback param in getStaticPaths changes the behavior of getStaticProps function. For reference, see the documentation

Dynamic Importing of an unknown component - NextJs

I want to load a component dynamically based on the route. I'm trying to make a single page which can load any individual component for testing purposes.
However whenever I try to do import(path) it shows the loader but never actually loads. If I hard code the exact same string that path contains then it works fine. What gives? How can I get nextjs to actually dynamically import the dynamic import?
// pages/test/[...component].js
const Test = () => {
const router = useRouter();
const { component } = router.query;
const path = `../../components/${component.join('/')}`;
console.log(path === '../../components/common/CircularLoader'); // prints true
// This fails to load, despite path being identical to hard coded import
const DynamicComponent = dynamic(() => import(path), {
ssr: false,
loading: () => <p>Loading...</p>,
});
// This seems to work
const DynamicExample = dynamic(() => import('../../components/Example'), {
ssr: false,
loading: () => <p>Loading...</p>,
});
return (
<Fragment>
<h1>Testing {path}</h1>
<div id="dynamic-component">
<DynamicComponent /> <!-- this always shows "Loading..." -->
<DynamicExample /> <!-- this loads fine. -->
</div>
</Fragment>
);
};
export default Test;
I put dynamic outside of the component, and it work fine.
const getDynamicComponent = (c) => dynamic(() => import(`../components/${c}`), {
ssr: false,
loading: () => <p>Loading...</p>,
});
const Test = () => {
const router = useRouter();
const { component } = router.query;
const DynamicComponent = getDynamicComponent(component);
return <DynamicComponent />
}
I had the same issue like the thread opener.
The Documentation describe, that it's not possible to use template strings in the import() inside dynamic:
In my case it was also impossible to add an general variable with the path there...
Solution
I've found an easy trick to solve this issue:
// getComponentPath is a method which resolve the path of the given Module-Name
const newPath = `./${getComponentPath(subComponent)}`;
const SubComponent = dynamic(() => import(''+newPath));
All the MAGIC seems to be the concatenation of an empty String with my generated Variable newPath: ''+newPath
Another Solution:
Another Solution (posted by bjn from the nextjs-Discord-Channel):
const dynamicComponents = {
About: dynamic(() => import("./path/to/about")),
Other: dynamic(() => import("./path/to/other")),
...
};
// ... in your page or whatever
const Component = dynamicComponents[subComponent];
return <Component />
This example might be useful, if you know all dynamically injectable Components.
So you can list them all and use it later on in your code only if needed)
The below code worked for me with dynamic inside the component function.
import dynamic from "next/dynamic";
export default function componentFinder(componentName, componentPath) {
const path = componentPath; // example : "news/lists"
const DynamicComponent = dynamic(() => import(`../components/${path}`),
{
ssr: false,
loading: () => <p>Loading Content...</p>,
});
return <DynamicComponent />;
}
It happens because router.query is not ready and router.query.component is undefined at the very first render of dynamic page.
This would print false at first render and true at the following one.
console.log(path === '../../components/common/CircularLoader');
You can wrap it with useEffect to make sure query is loaded.
const router = useRouter();
useEffect(() => {
if (router.asPath !== router.route) {
// router.query.component is defined
}
}, [router])
SO: useRouter receive undefined on query in first render
Github Issue: Add a ready: boolean to Router returned by useRouter
As it was said here before the dynamic imports need to be specifically written without template strings. So, if you know all the components you need beforehand you can dynamically import them all and use conditionals to render only those you want.
import React from 'react';
import dynamic from 'next/dynamic';
const Component1 = dynamic(() => import('./Component1').then((result) => result.default));
const Component2 = dynamic(() => import('./Component2').then((result) => result.default));
interface Props {
slug: string;
[prop: string]: unknown;
}
export default function DynamicComponent({ slug, ...rest }: Props) {
switch (slug) {
case 'component-1':
return <Component1 {...rest} />;
case 'component-2':
return <Component2 {...rest} />;
default:
return null;
}
}

How can I get (query string) parameters from the URL in Next.js?

When I click on a link in my /index.js, it brings me to /about.js page.
However, when I'm passing parameter name through URL (like /about?name=leangchhean) from /index.js to /about.js, I don't know how to get it in the /about.js page.
index.js
import Link from 'next/link';
export default () => (
<div>
Click{' '}
<Link href={{ pathname: 'about', query: { name: 'leangchhean' } }}>
<a>here</a>
</Link>{' '}
to read more
</div>
);
Use router-hook.
You can use the useRouter hook in any component in your application.
https://nextjs.org/docs/api-reference/next/router#userouter
pass Param
import Link from "next/link";
<Link href={{ pathname: '/search', query: { keyword: 'this way' } }}><a>path</a></Link>
Or
import Router from 'next/router'
Router.push({
pathname: '/search',
query: { keyword: 'this way' },
})
In Component
import { useRouter } from 'next/router'
export default () => {
const router = useRouter()
console.log(router.query);
...
}
Using Next.js 9 or above you can get query parameters:
With router:
import { useRouter } from 'next/router'
const Index = () => {
const router = useRouter()
const {id} = router.query
return(<div>{id}</div>)
}
With getInitialProps:
const Index = ({id}) => {
return(<div>{id}</div>)
}
Index.getInitialProps = async ({ query }) => {
const {id} = query
return {id}
}
url prop is deprecated as of Next.js version 6:
https://github.com/zeit/next.js/blob/master/errors/url-deprecated.md
To get the query parameters, use getInitialProps:
For stateless components
import Link from 'next/link'
const About = ({query}) => (
<div>Click <Link href={{ pathname: 'about', query: { name: 'leangchhean' }}}><a>here</a></Link> to read more</div>
)
About.getInitialProps = ({query}) => {
return {query}
}
export default About;
For regular components
class About extends React.Component {
static getInitialProps({query}) {
return {query}
}
render() {
console.log(this.props.query) // The query is available in the props object
return <div>Click <Link href={{ pathname: 'about', query: { name: 'leangchhean' }}}><a>here</a></Link> to read more</div>
}
}
The query object will be like: url.com?a=1&b=2&c=3 becomes: {a:1, b:2, c:3}
For those looking for a solution that works with static exports, try the solution listed here: https://github.com/zeit/next.js/issues/4804#issuecomment-460754433
In a nutshell, router.query works only with SSR applications, but router.asPath still works.
So can either configure the query pre-export in next.config.js with exportPathMap (not dynamic):
return {
'/': { page: '/' },
'/about': { page: '/about', query: { title: 'about-us' } }
}
}
Or use router.asPath and parse the query yourself with a library like query-string:
import { withRouter } from "next/router";
import queryString from "query-string";
export const withPageRouter = Component => {
return withRouter(({ router, ...props }) => {
router.query = queryString.parse(router.asPath.split(/\?/)[1]);
return <Component {...props} router={router} />;
});
};
Get it by using the below code in the about.js page:
// pages/about.js
import Link from 'next/link'
export default ({ url: { query: { name } } }) => (
<p>Welcome to About! { name }</p>
)
I know 2 ways to do this:
A Server-Side way, and a Client-Side way.
Method #1: SSR (Server-Side Rendering):
You should use Query Context for that page.
So use getServerSideProps instead of getStaticProps
import React from "react";
export async function getServerSideProps(context) {
const page = (parseInt(context.query.page) || 1).toString();
// Here we got the "page" query parameter from Context
// Default value is "1"
const res = await fetch(`https://....com/api/products/?page=${page}`);
const products = await res.json();
return {props: {products: products.results}}
// will be passed to the page component as props
}
const Page = (props) =>{
const products = props.products;
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>);
}
export default Page
The reason is that: this data cannot be pre-rendered ahead of user's request, so it must be Server-Side Rendered (SSR) on every request.
Static Pages: Use getStaticProps
Changing Content: use getServerSideProps
And here the content is changing based on query Parameters
Reference: https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props
Method #2: Next Router (Client Side):
import {useState, useEffect} from "react";
import { useRouter } from 'next/router'
const Page = () =>{
const [products, setProducts] = useState([]);
const [page, setPage] =useState((useRouter().query.page || 1).toString());
// getting the page query parameter
// Default value is equal to "1"
useEffect(()=>{
(async()=>{
const res = await fetch(`https://....com/api/products/?page=${page}`);
const products = await res.json();
setProducts(products.results);
// This code will be executed only once at begining of the loading of the page
// It will not be executed again unless you cahnge the page
})()
},[page]);
return (
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
export default Page
Reference: https://nextjs.org/docs/api-reference/next/router
If you need to retrieve a URL query from outside a component:
import router from 'next/router'
console.log(router.query)
import { useRouter } from 'next/router';
function componentName() {
const router = useRouter();
console.log('router obj', router);
}
We can find the query object inside a router using which we can get all query string parameters.
Using {useRouter} from "next/router"; helps but sometimes you won't get the values instead u get the param name itself as value.
This issue happens when u are trying to access query params via de-structuring like:
let { categoryId = "", sellerId = "" } = router.query;
and the solution that worked for me is try to access the value directly from query object:
let categoryId = router.query['categoryId'] || '';
let sellerId = router.query['sellerId'] || '';
Post.getInitialProps = async function(context) {
const data = {}
try{
data.queryParam = queryString.parse(context.req.url.split('?')[1]);
}catch(err){
data.queryParam = queryString.parse(window.location.search);
}
return { data };
};
import { useRouter } from 'next/router'
const Home = () => {
const router = useRouter();
const {param} = router.query
return(<div>{param}</div>)
}
Also you can use getInitialProps, more details refer the below tutorial.
get params from url in nextjs
What worked for me in Nextjs 13 pages in the app directory (SSR)
Pass params and searchParams to the page:
export default function SomePage(params, searchParams) {
console.log(params);
console.log(searchParams);
return <div>Hello, Next.js!</div>;
With some builds there may be a bug that can be solved by adding:
export const dynamic='force-dynamic';
especially when deploying on Vercel.
ref: https://beta.nextjs.org/docs/api-reference/file-conventions/page#searchparams-optional
https://github.com/vercel/next.js/issues/43077

Resources