I'm implementing a localized react app using react-intl. I have a folder with all translations. I want to dynamically import all the translations and also get the filenames to display a dropdown with available languages on the page.
Currently I have to hardcode each language:
import {IntlProvider, FormattedMessage} from 'react-intl'
import messages_de from '../translations/de.json'
import messages_en from '../translations/en.json'
const messages = {
'de': messages_de,
'en': messages_en,
};
const i18nConfig = {
defaultLocale: 'en',
messages,
};
Is there a way to get a list of files (e.g. before compiling an app) which I can then access within react components?
I ended up with the following solution:
// search for .json files
const webpackContext = require.context('../translations', false, /\.json$/)
const filenames = webpackContext.keys() // => ['./de.json, './en.json']
const key_value_pairs = filenames.map(name => [name.match(/\/(\w+)\.json$/)[1], webpackContext(name)])
// make a dictionary from the array of arrays
const messages = Object.fromEntries(key_value_pairs)
This code imports dynamically all files into a dictionary where the key is the filename.
require.context takes a directory to search, a flag indicating whether subdirectories should be searched too, and a regular expression to match files against.
The returned function webpackContext is the most confusing part. Although it is a function, it also has properties, because everything is an object in js. webpackContext.keys() returns a list of filenames in given directory matching given regex.
Finally, webpackContext(name) imports a file.
The code above is equivalent to the following code:
import messages_de from '../translations/de.json'
import messages_en from '../translations/en.json'
const messages = {
'de': messages_de,
'en': messages_en,
};
Related
As the title states, I am trying to get the text from a docx file I store locally in my src folder. Currently I am using Docxtemplater to get text in the following way:
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils/index.js";
import docx from "../../assets/documents/T&C.docx";
function loadFile(url, callback) {
PizZipUtils.getBinaryContent(url, callback);
}
...
const [docText, setDocText] = useState("");
loadFile(docx, function (error, content) {
if (error) {
throw error;
}
var zip = new PizZip(content);
var doc = new Docxtemplater().loadZip(zip, {
paragraphLoop: true,
linebreaks: true,
});
setDocText(doc.getFullText());
My problem for this is that I am getting a chunk of text that is neither indented or styled (meaning it is missing new line and lists are also not in the correct format). Is there any other approach to get the text from a docx file by getting the style also (new lines lists and whatever it has there, excluding images or math formulas).
I am open to using other libraries if it makes my job easier.
I have have a large application that monitors a file called routes.js . I can not change the file name or mess with routes.js at all. I need to load another file based on a useState variable from another component when a condition is met. This following code will need to be put in Apps.js example:
if (!change) {
import routes from "routes";
} else {
import routes from "newroutes"
}
Is this possible?
You can just alias the imports.
import routes_1 from "routes";
import routes_2 from "newroutes"
Then, you can just declare a variable: routes and assign the appropriate value to it.
routes = !change ? routes_1 : routes_2;
It's possible to do using Webpack lazy loading (if you build your app using Webpack):
import(/* webpackChunkName: "routes" */ './routes').then(module => {
const routes = module.default;
});
But probably you will need to adjust your build config. Also this import is returning a promise, so you should write your code in the callback.
Update: but in your case it seems you don't need it. You could do something like this:
import file1 from 'routes'
import file2 from 'newroutes'
let routes = file1
if (change) {
routes = file2
}
So you will have your routes variable unchanged
Does app fabric have something that supports translation of data directly (NOTE: I don't want React's Intl since that is meant for localising data before it is rendered). I need to localise the data present in the state and update the state with the translated data.
React's intl is intended to be used with components and I require translation of data present outside components. I don't want to use the createIntl module since that requires manual provision of locale & messages.
This is the code that I'm dealing with. It is present outside a component.
export function func () {
client.get (url, {}).then (
response => {
// handle error
}).then(data => {
// This is where i want to translate the data based on the locale i am in
// so that the data this translated data is present in the state
}
// error-handling, more code ...
}
You can use i18next to translate data in plain js.
Basic Example:
import i18next from 'i18next';
i18next.init({
lng: 'en', // if you're using a language detector, do not define the lng option
debug: true,
resources: {
en: {
translation: {
"key": "hello world"
}
}
}
});
// initialized and ready to go!
// i18next is already initialized, because the translation resources where passed via init function
document.getElementById('output').innerHTML = i18next.t('key');
By the way, why not translate data on the server-side? You may encounter some cases like export excel which may require translation, too.
I have two JSON files with names 'en.json' and 'fr.json' which has all the translations.
en.json
{ "General.LearnMore": "Learn More" }
I have a mobx store (.ts file) and I want to access intl.formatMessage() in that class.
It is easier in functional components. I can use useIntl() hook but how can I do the same in a store file (non-component).
When I write the below code in store file:
const messages = defineMessages({
SMS: {
id: 'General.LearnMore',
},
});
const cache = createIntlCache();
const locale = localStorage.getItem('locale')!;
const intl = createIntl({ locale, messages: {} }, cache);
console.log(intl.formatMessage({ id: 'General.select' }));
console.log(intl.formatMessage({ id: messages.sms.id }));
It gives this error:
What I am missing and How can I fix this? Please help!
In createIntl you pass an empty object to messages so intl doesn't find the ids.
To fix, import the en.json:
import enMessages from '../path/to/en.json'
and pass it on cretaeIntl:
const intl = createIntl({ locale, messages: enMessages }, cache);
If you created the messages object with defineMessages just for the purpose of getting value from intl - you can delete it, it doesn't give access to the key-value files (createIntl doesn't automatically get the data of intlProvider, it needs full initialition with locale and messages)
If you want the messages to be updated every time the locale is switched so here a good example
Hope this will help!
So i have a visual studio project created with react.js.
I am trying to link to an image dynamically, but failing. This is the code I am trying:
At the top im setting a variable for the first part of the path:
this.LogoPath = '../images/'
And then im dynamically grabbing the name of the image from an api call.
this.setState({ imageNamePath: this.state.location.imageName })
And then im concatinating them:
{`${this.LogoPath}${this.state.location.imageName}`}
In the console, im getting:
img src='../images/the-images-name-here.png'
So it seems to be working, but it is not. I get no errors, and I have broken images. My best guess is that react is changing the images to something like:
image-name-here.6a131799.png
Surely someone has run across this before, but my google search pulled up little help.
So how do i get the images to show?
Webpack is a smart tool. One of it's strength is the trash code/files elimination from the bundle.
That means that if a file is not imported using import myFile from '../myPath/myFile.jpg'; or require('../myPath/myFile.jpg');` it won't be a part of the final bundle.
In your case you're not importing the file. You're creating a string variable instead which means nothing to webpack.
There are different options that could work in your case:
Option 1: Pre-import all images and map them in an object:
import React, {Component} from 'react';
import image1 from '../assets/image1.png';
import image2 from '../assets/image2.png';
import image3 from '../assets/image3.png';
const imageTypes = {
image1,
image2,
image3,
}
export default class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
imageType: 'image1'
}
}
render() {
return (
<img src={imageTypes[this.state.imageType]} />
)
}
}
Option 2: Not recommended - Use require to dynamically import files (webpack configurations might be needed in order to include all possible required images in the production bundle):
class MyComponent {
constructor(props) {
super(props);
this.state = {
image: 'file1.png'
}
}
render() {
return (
<img src={require(`./assets/${this.state.image}`)} />
)
}
}
Option 3: Send the image blob (in base64) from the API.
I suggest you to use the Option 1 or Option 3, based on your requirements, such as: how often will be images be changed/added/removed. It's not normal to import files dynamically from ReactJS bundle and you might end up having a non-existing image in your project requested by the data coming from an external source.
For Option 2 you also might have some problems configuring the webpack in order to include in the bundle all the images that you'll probably need to render, even though they are not imported (hardcoded) somewhere in the JS files. Keep in mind that the React Application in production ends up being a collection of static files. You'll need to treat them as follows.