Change the URL path using I18next-react - javascript

Hello there, I can't change the href path (URL) after selecting a new language
import i18n from 'i18next';
import { useTranslation, initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import I18NextHttpBackend from 'i18next-http-backend';
i18n.on('languageChanged', function (lng) {
if (lng === i18n.options.fallbackLng[0]) {
if (window.location.pathname.includes('/' + i18n.options.fallbackLng[0])) {
const newUrl = window.location.pathname.replace(
'/' + i18n.options.fallbackLng[0]
);
window.location.replace(newUrl);
}
}
});
i18n
.use(I18NextHttpBackend)
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: ['en'],
whitelist: ['en', 'de', 'it', 'es'],
detection: {
order: ['path', 'cookie', 'htmlTag', 'localStorage', 'subdomain'],
caches: ['cookie'],
lookupFromPathIndex: 0,
checkWhitelist: true,
},
backend: {
loadPath: '/localization/{{lng}}/translation.json',
},
interpolation: {
escapeValue: false,
},
});
export default i18n;
I got example.com/undefined/page1
I used this way to import the language path to the Href
export const baseUrl = i18n.language === 'en' ? '' : '/' + i18n.language;
and the link <a>home</a>
<a className='item' href={baseUrl + '/'} > Home </a>

We have created a LanguageChanger component. A handleClick function determines the url and updates as you have stated but on LanguageChanged does not provide the same flexibility imo.
I have included two versions of newUrl. First, en is not present in the url. Second, if languageCode is present in the url.
/**
* Handles the language change
* #param code - updated language
* #global lang - current set language in state
*/
let handleChange = (code) => {
if (lang !== code) {
// v1 - below lines since 'en' is not present in the url as your baseUrl definition
let newUrl;
if (lang === "en") {
newUrl = window.location.pathname.replace(`/`, `/${code}/`);
} else if (code === "en") {
newUrl = window.location.pathname.replace(`/${lang}`, ``);
} else {
newUrl = window.location.pathname.replace(`/${lang}`, `/${code}`);
}
// v2 - below is the version if language is always present in the url
// let newUrl = window.location.pathname.startsWith(`/${lang}`) ? window.location.pathname.replace(`/${lang}`, `/${code}`) : window.location.pathname.replace(`/`, `/${code}/`)
window.location.replace(newUrl);
localStorage.setItem("i18nextLng", code); // this writes to the localStorage to keep the change
setLang(code); // this is coming from useState
i18n.changeLanguage(code);
}
};

Related

Problem loading localized messages for vue I18n

when I initialize vue-I18n plugin I can't load localized messages for some reason.
I'm using context.require to get all json objects inside folder but it's not working for some reason? Is it because I also have folders with the same name as the json files: en.json,es.json along with es and en folders laravel uses for backend translations.
My folder structure
https://i.imgur.com/BKkZYXC.png
And I18n.js file where I initialize the plugin is in resources
This is how I load messages:
function loadLocaleMessages() {
const locales = require.context(
"../lang",
true,
/[A-Za-z0-9-_,\s]+\.json$/i
);
console.log('locales',locales);
const messages = {};
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
console.log('locale',locale);
messages[locale] = locales(key);
}
});
return messages;
}
I18n.js file
import Vue from "vue";
import VueI18n from "vue-i18n";
Vue.use(VueI18n);
//Antes habia un locales en resources
function loadLocaleMessages() {
const locales = require.context(
"../lang",
true,
/[A-Za-z0-9-_,\s]+\.json$/i
);
console.log('locales',locales);
const messages = {};
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
if (matched && matched.length > 1) {
const locale = matched[1];
console.log('locale',locale);
messages[locale] = locales(key);
}
});
return messages;
}
function getStartingLocale() {
if (localStorage.getItem('last-locale')) {
return localStorage.getItem('last-locale')
}
return "es";
}
export default new VueI18n({
locale: getStartingLocale(),
fallbackLocale: "es",
messages: loadLocaleMessages()
});

How to import multiple locale json files in Vue 3 + i18n?

This is my code, that works without problems:
import { createI18n } from 'vue-i18n'
import messages from './components/json/foo/foo_messages.json'
const app = createApp(App)
installI18n(app)
const i18n = createI18n({
locale: 'ru',
messages
})
app
.use(i18n)
.use(vuetify)
.mount('#app')
Now I need to load messages also from ./components/json/bar/bar_messages.json. I tried to do this way:
import { createI18n } from 'vue-i18n'
import foo_msg from './components/json/foo/foo_messages.json'
import bar_msg from './components/json/bar/bar_messages.json'
const app = createApp(App)
installI18n(app)
const i18n = createI18n({
locale: 'ru',
messages: {foo_msg, bar_msg}
})
app
.use(i18n)
.use(vuetify)
.mount('#app')
But it didn't work. Could anyone say how to do it?
EDIT: This is my foo json file
{
"ru": {
"header": {
"hello": "Привет"
}
},
"en": {
"header": {
"hello": "Hello"
}
}
}
and this is bar json file
{
"ru": {
"footer": {
"bye": "Пока"
}
},
"en": {
"footer": {
"bye": "Goodbye"
}
}
}
What you are trying to do is not very scalable. Given the format of the i18n JSON messages, you need to merge the input files to something like this:
{
"ru": {
"header": {
"hello": "Привет"
},
"footer": {
"bye": "Пока"
}
},
"en": {
"header": {
"hello": "Hello"
},
"footer": {
"bye": "Goodbye"
}
}
}
...this is definitely possible with JS but you must still import the JSON file for each component in your main.js which is tedious and error prone
Did you consider using vue-i18n custom blocks in your components? You can even keep the translations in external JSON files and use a custom block like <i18n src="./myLang.json"></i18n>
this is much better approach but if you stil want to use yours, here is a simple code how to merge all translation files (objects imported from JSON) into a single object usable by vue-i18n:
// import foo_msg from './components/json/foo/foo_messages.json'
const foo_msg = {
"ru": {
"header": {
"hello": "Привет"
}
},
"en": {
"header": {
"hello": "Hello"
}
}
}
// import bar_msg from './components/json/bar/bar_messages.json'
const bar_msg = {
"ru": {
"footer": {
"bye": "Пока"
}
},
"en": {
"footer": {
"bye": "Goodbye"
}
}
}
const sources = [foo_msg, bar_msg]
const messages = sources.reduce((acc, source) => {
for(key in source) {
acc[key] = { ...(acc[key] || {}), ...source[key] }
}
return acc
},{})
console.log(messages)
The accepted solution is already a good solution, but if you assist to use .json files to translate text. Here is my solution.
Use vue-cli to add i18n dependency, it would generate all the requirement files that we need.
vue add vue-i18n
It would generate the locales folder inside src, which it stores all the translation json files.
Then it would generate couple env variable on .env file and a i18n.js file
here is the i18n.js it generates
import { createI18n } from 'vue-i18n'
/**
* Load locale messages
*
* The loaded `JSON` locale messages is pre-compiled by `#intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
* See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
*/
function loadLocaleMessages() {
const locales = require.context('./locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
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).default
}
})
return messages
}
export default createI18n({
locale: process.env.VUE_APP_I18N_LOCALE || 'en',
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})
In our main.js, i had seen that vue has already add the component for me
import i18n from './i18n'
const app = createApp(App).use(i18n)
*Edit
I am using vite for building vue project, the loadLocaleMessages does not work in my case.
I made some modification. It needs to manually import all the json files, but i did not find any alternative solution.
I also change the env variable with 'VITE' prefix, and process.env to import.meta.env.
// import all the json files
import en from './locales/en.json'
import zh from './locales/zh.json'
/**
* Load locale messages
*
* The loaded `JSON` locale messages is pre-compiled by `#intlify/vue-i18n-loader`, which is integrated into `vue-cli-plugin-i18n`.
* See: https://github.com/intlify/vue-i18n-loader#rocket-i18n-resource-pre-compilation
*/
function loadLocaleMessages() {
const locales = [{ en: en }, { zh: zh }]
const messages = {}
locales.forEach(lang => {
const key = Object.keys(lang)
messages[key] = lang[key]
})
return messages
}
export default createI18n({
locale: import.meta.env.VITE_APP_I18N_LOCALE || 'en',
fallbackLocale: import.meta.env.VITE_APP_I18N_FALLBACK_LOCALE || 'en',
messages: loadLocaleMessages()
})

How to connect to Graphql server using Ember-Apollo?

I am working with a full stack GraqlQL based application. The server is working fine and now I need to try out the first queries and mutations on the client side. For some reason, the "monitoring" route, and everything that follows it, is not displayed. Below I will show the files that I have edited or created.
items.graphql:
query {
items {
_id
name
}
}
environment.js:
'use strict';
module.exports = function(environment) {
let ENV = {
apollo: {
apiURL: 'http://localhost:5000/graphql'
},
modulePrefix: 'client',
environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
//
},
EXTEND_PROTOTYPES: {
Date: false
}
},
APP: {
//
}
};
if (environment === 'development') {
//
}
if (environment === 'test') {
ENV.locationType = 'none';
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
ENV.APP.autoboot = false;
}
if (environment === 'production') {
//
}
return ENV;
};
monitoring.js (route):
import Route from '#ember/routing/route';
import { queryManager } from 'ember-apollo-client';
import query from 'client/gql/items.graphql';
export default Route.extend({
apollo: queryManager(),
model() {
return this.apollo.watchQuery({ query }, 'items');
}
});
monitoring.hbs:
<h3>Monitoring</h3>
<div>
{{#each model as |item|}}
<h3>{{item.name}}</h3>
{{/each}}
</div>
{{outlet}}
Thank you for attention!
I see this error:
Uncaught (in promise) Error: fetch is not defined - maybe your browser targets are not covering everything you need?
The solution is to fix two things.
First is to put this in ember-cli-build.js:
'ember-fetch': {
preferNative: true
}
And fix the route file:
import Route from '#ember/routing/route';
import { queryManager } from 'ember-apollo-client';
import query from 'client/gql/queries/items.graphql';
export default Route.extend({
apollo: queryManager(),
async model() {
let queryResults = await this.apollo.watchQuery({ query }, 'items');
return Object.values(queryResults);
}
});

Using two language codes for same locale in i18n

I am utilizing i18n to render translations English/Japanese -- My default language is English in en. but recently realized my website is not using the correct ISO language code for Japanese. jp instead of ja.
I am wondering if it's possible to have two language codes in i18n reference the same locale files -- and perplexed how to do this.
For example, I'd like /whatever?lng=jp and /whatever?lng=ja to work for Japanese text.
Here are my configs:
i18nConfig.js
const path = require("path");
module.exports = {
fallbackLng: "en",
preload: ["en", "ja", "jp"],
ns: [
"localefiles"
],
backend: {
loadPath: path.join(__dirname, "locales", "{{lng}}", "{{ns}}.json"),
addPath: path.join(__dirname, "locales", "{{lng}}", "{{ns}}.missing.json"),
},
};
i18n.js
const i18n = require("i18next");
const XHR = require("i18next-xhr-backend");
const LanguageDetector = require("i18next-browser-languagedetector");
const options = {
fallbackLng: "en",
load: "languageOnly",
ns: ["common"],
defaultNS: "common",
debug: process.env.NODE_ENV !== "production" && false,
saveMissing: true,
interpolation: {
escapeValue: false,
formatSeparator: ",",
format: (value, format) => {
if (format === "uppercase") {
return value.toUpperCase();
}
return value;
},
},
};
if (process.browser) {
i18n.use(XHR).use(LanguageDetector);
}
if (!i18n.isInitialized) {
i18n.init(options);
}
if (i18n.initialLanguage == "jp") {
console.log({i18n})
i18n.setLng("ja");
}
i18n.getInitialProps = (req, namespaces) => {
if (!namespaces) {
namespaces = i18n.options.defaultNS;
}
if (typeof namespaces === "string") {
namespaces = [namespaces];
}
req.i18n.toJSON = () => null;
const initialI18nStore = {};
req.i18n.languages.forEach((lan) => {
initialI18nStore[lan] = {};
namespaces.forEach((ns) => {
initialI18nStore[lan][ns] = req.i18n.services.resourceStore.data[lan]
? req.i18n.services.resourceStore.data[lan][ns] || {}
: {};
});
});
return {
i18n: req.i18n,
initialI18nStore,
initialLanguage: req.i18n.language,
};
};
module.exports = i18n;
Thanks so much for help on this basic question.
You can define backend.loadPath as a function which gets langs & namespace and returns a string with the path to the file.
// i18next.js
const i18n = require('i18next');
const XHR = require('i18next-xhr-backend');
const LanguageDetector = require('i18next-browser-languagedetector');
const options = {
fallbackLng: 'en',
load: 'languageOnly',
ns: ['common'],
defaultNS: 'common',
debug: process.env.NODE_ENV !== 'production' && false,
saveMissing: true,
backend: {
loadPath(lng, ns) {
if (lng === 'jp') { // <---
return '/locales/ja/{{ns}}.json';
}
return '/locales/{{lng}}/{{ns}}.json';
},
addPath: path.join(__dirname, 'locales', '{{lng}}', '{{ns}}.missing.json'),
},
interpolation: {
escapeValue: false,
formatSeparator: ',',
format: (value, format) => {
if (format === 'uppercase') {
return value.toUpperCase();
}
return value;
},
},
};

RepositoryNotFoundError : TypeORM

I am trying to get the following example working:
https://github.com/typeorm/javascript-example/tree/master/src/app3-es6
I am running into the following error:
Error
at new RepositoryNotFoundError (...\node_modules\typeorm\connection\error\RepositoryNotFoundError.js:24:23)
at Connection.findRepositoryAggregator (...\node_modules\typeorm\connection\Connection.js:513:19)
at Connection.getRepository (...\node_modules\typeorm\connection\Connection.js:405:21)
at ...\index.js:27:37
name: 'RepositoryNotFoundError',
message: 'No repository for "Post" was found. Looks like this entity is not registered in current "default" connection?'
here is index.js
const typeorm = require("typeorm"); // import * as typeorm from "typeorm";
const Post = require("./model/Post"); // import {Post} from "./model/Post";
// import Post from './model/Post.js';
const Category = require("./model/Category"); // import {Category} from "./model/Category";
typeorm.createConnection({
driver: {
type: "oracle",
host: "localhost",
port: 1521,
username: "uname",
password: "pwd",
sid: "dev"
},
entities: [
__dirname + "/entity/*.js"
],
autoSchemaSync: true
}).then(function (connection) {
console.log(connection);
let post = new Post.Post();
post.title = "Control flow based type analysis";
post.text = "TypeScript 2.0 implements a control flow-based type analysis for local variables and parameters.";
post.categories = [new Category.Category(0, "TypeScript"), new Category.Category(0, "Programming")];
let postRepository = connection.getRepository(Post.Post);
postRepository.persist(post)
.then(function(savedPost) {
console.log("Post has been saved: ", savedPost);
console.log("Now lets load all posts: ");
return postRepository.find();
})
.then(function(allPosts) {
console.log("All posts: ", allPosts);
});
}).catch(function(error) {
console.log("Error: ", error);
});
Post.js in /model/
/*export */ class Post {
constructor(id, title, text, categories) {
this.id = id;
this.title = title;
this.text = text;
this.categories = categories;
}
}
module.exports = {
Post: Post
};
Category.js
/*export */ class Category {
constructor(id, name) {
this.id = id;
this.name = name;
}
}
module.exports = {
Category: Category
};
PostSchema.js in /entity/
const Post = require("../model/Post"); // import {Post} from "../model/Post";
const Category = require("../model/Category"); // import {Category} from "../model/Category";
const PostSchema = {
target: Post,
columns: {
id: {
primary: true,
type: "int",
generated: true
},
title: {
type: "string"
},
text: {
type: "text"
}
},
relations: {
categories: {
target: Category,
type: "many-to-many",
joinTable: true,
cascadeInsert: true
}
}
};
module.exports = {
PostSchema: PostSchema
};
CategorySchema.js
const Category = require("../model/Category"); // import {Category} from "../model/Category";
const CategorySchema = {
target: Category,
columns: {
id: {
primary: true,
type: "int",
generated: true
},
name: {
type: "string"
}
}
};
module.exports = {
CategorySchema: CategorySchema
};
i dont know what i am doing wrong
It looks like your entity import is not working. If you import via the wildcard:
entities: [
__dirname + "/entity/*.js"
],`
Make sure your model is compiled to js. You also could just import
createConnection({
...,
entities: [
Post,
...
],}).then(...)
For those who are using typescript and experience this problem: Be reminded that you need to include both ts and js file suffixes when specifying the entities-path:
ts used when locally running with ts-node
js used when having
built for production via tsc.
Code:
import * as path from 'path';
// ...
entities: [
// assuming _dirname is your project root
path.resolve(__dirname, '**/*.entity{.ts,.js}'),
],
I had the same problem for months and finally figured out what I was doing wrong.
When you import Entities, make sure the file names are EXACTLY matching. It's not going to throw any errors, but during the run time, it's going to throw the above error.
Ex. In the entity or model classes, if we import like this,
import { FooClass } from "./foo-Class.model";
it's different from
import { FooClass } from "./foo-class.model";
It won't show any errors, but when you try to call the table, it will show the exact same error.
I had the same problem. None of the solutions worked for me. After much debugging I figured out that you'll receive this error if your connection is closed.
So if you are facing this error, make sure your connection is not closed.
try {
connection = getConnection(config.name)
//after adding this if block, I no longer received this error
if (!connection.isConnected) {
await connection.connect();
}
} catch(err) {
connection = await createConnection(config);
}
If it is closed, connect it again.

Categories