React-Intl pass translation as string variable and not object - reactjs

I'm in the process of adding react-intl to a payment app I'm building but hitting a snag. I apologize if this has been addressed somewhere. I scoured the issues and documentation and couldn't find a direct answer on this (probably just overlooking it).
Use Case: Once a payment is processed I'd like to give the user the option to tweet a translated message indicating they've donated.
Problem: Twitter uses an iframe to "share tweets", and requires a text field as a string variable. When I pass my translation I get [object Object] in the tweet instead of the translated text. This makes sense based on my understanding of the translation engine. But I cant seem to find a way to pass a string rather than a translation object.
what I get when I use {translate('example_tweet')}
const translationText = object
what I need
const translationText = 'this is the translated text'
Question
How do I get the translated text as a string variable rather than an object to be rendered on a page?
Code
button
import { Share } from 'react-twitter-widgets'
import translate from '../i18n/translate'
export default function TwitterButton () {
return (
<Share
url='https://www.sampleSite.org' options={{
text: {translate('example_tweet')},
size: 'large'
}}
/>
)
}
translate
import React from 'react'
import { FormattedMessage } from 'react-intl'
const translate = (id, value = {}) => <FormattedMessage id={id} values={{ ...value }} />
export default translate

I was able to solve it without messing with react-intl. I built a function that scrapes the text I need from the page itself. So it really doesnt matter what the language is. I was hoping to figure out how to snag the translations as variables, but this gets the job done.
function makeTweetableUrl (text, pageUrl) {
const tweetableText = 'https://twitter.com/intent/tweet?url=' + pageUrl + '&text=' + encodeURIComponent(text)
return tweetableText
}
function onClickToTweet (e) {
e.preventDefault()
window.open(
makeTweetableUrl(document.querySelector('#tweetText').innerText, pageUrl),
'twitterwindow',
'height=450, width=550, toolbar=0, location=0, menubar=0, directories=0, scrollbars=0'
)
}
function TwitterButton ({ text, onClick }) {
return (
<StyledButton onClick={onClick}>{text}</StyledButton>
)
}

Related

How to access current value from writable?

I want to create a custom wrapper for i18n to translate content of the site by clicking the lang button.
Currently, I have something like this.
<script>
import { localization } from './localiztion.ts';
</script>
<p>{localization.t("hello")}</p>
<button on:click={localization.toggleLocale}></button>
p which holds a text (which should be translated) and button which triggers translation.
To split logic from UI I moved localization logic into a different file. It looks like this
const resources = {
"en": {
"hello": "Hello",
},
"uk": {
"hello": "Привіт"
}
}
export function createLocalization() {
let store = writable("en");
return {
unsubscribe: store.unsubscribe,
toggleLocale: () => {
store.update((previousLocale) => {
let nextLocale = previousLocale === "en" ? "uk" : "en";
return nextLocale;
});
},
t: (key: string): string => {
// How to get access to the current store value and return it back to UI?
// I need to do something like this
return resources[store][key]
}
}
}
export const localization = createLocalization();
The problem I have I need to access the current local from within a t function. How can I do this?
I could pass it from UI like
// cut
<p>{localization.t("hello", $localization)}</p>
// cut
by doing this I achieve what I want, but the solution is too cumbersome.
Any advice on how I can do this?
You could get the store value via get, but this is be a bad idea, as it would lose reactivity. I.e. a language change would not update your text on the page.
A better approach is defining it as a store. Since stores currently have to be at the top level to be used with $ syntax, it is more ergonomic to split it into a separate derived store:
export let locale = writable("en"); // Wrap it to restrict it more
export let translate = derived(
locale,
$locale => key => resources[$locale][key],
);
This way you can import this store, which contains a function for translating keys:
import { translate } from '...';
// ...
$translate('hello')
REPL
(The stores can of course also be created differently and e.g. injected via a context instead of importing them.)

In react, how to pass a dynamic link in sms href?

React-share is a nifty tool but.... doesn't have sharable sms support that I can find. Need some pointers. //will send virtual coffee as a thanks!B)
The challenge: share a dynamically created product url as a text message, with the dynamic description and url.
I have tried several link additions, but not able to accomplish the result I am looking for. Here is most recent try:
<FontAwesome name="share" />
Another one I tried (which is what I use for non-dynamic links) at least drops text into the body is:
<FontAwesome name="share" />
After working this through, here is what we ended up going with, no phone number at all, just the description and url in the body of the message.
First:
const objectEncode = object => {
const str = [];
Object.keys(object).forEach(key => {
str.push(`${encodeURIComponent(key)}=${encodeURIComponent(object[key])}`);
});
return str.join('&');
};
export default objectEncode;
Then:
import objectEncode from '../../../utils/objectEncode';
Then within the component:
render(){
const smsBody = objectEncode({ body: `${description} ${url}` });
return (
<a href={`sms://?&${smsBody}`} className="social-link">
<FontAwesome name="commenting" />
</a>
);
}

how do I interpolate a Link component in Next-i18next / React i18next that changes position in the text

Currently I'm using Next.js with Next-i18next for I18N, but I understand that the React/i18next implementation is basically the same.
The problem I'm having is that I need to interpolate a next Link component inside some translation text, but depending on the language (English vs German), the order of the text and the link would change.
For instance the text I'm struggling with is: 'Accept data policy' vs 'Datenschutzerklärung akzeptieren'
As of the moment I have a quick fix by creating two values in the translation JSON files for the text and the link and then swapping the position based on the current language. Obviously this is not a sustainable solution. I have tried to utilise the 'Trans' component but this is showing some unexpected behaviour where the translation only kicks in after the page is refreshed, otherwise you see the text inside the Trans component.
example:
function LinkText({ href, children}) {
return <Link to={href || ''}>{children}</Link>;
}
return (
<Trans i18nKey="sentence">
text before link
<LinkText href="/data-policy">{t("dataPolicy")}</LinkText>
text after link
</Trans>
);
and the JSON in question:
{
"sentence": "agree to our <1><0/></1>",
"dataPolicy": "data policy"
}
Here's a link to CodeSandbox I made to replicate the problem with in React: link
(P.S The implementation of i18next doesn't seem to effectively swap out the languages in Codesandbox at the moment, but I included it as the code is there for a MWE)
Thanks in advance for your help, this has been driving me insane.
You had few missing parts,
Your i18next config was lack of a way to fetch the locale files, I've added i18next-http-backend.
You should use Trans component to inject the link to the sentence.
Your locale json should look like this:
{
"sentence": "Accept <0>data policy</0>"
}
// TranslatedLink.js
import React from 'react';
import { useTranslation, Trans } from 'react-i18next';
import { Link } from 'react-router-dom';
function LinkText({ href, children }) {
return <Link to={href || ''}>{children}</Link>;
}
export default function TranslatedLink() {
const { t } = useTranslation(['common']);
return (
<div style={{ padding: 50 }}>
<Trans i18nKey="sentence" t={t} components={[<LinkText href="/data-policy" />]} />
</div>
);
}
A working example: https://codesandbox.io/s/react-i18n-interpolation-issue-forked-ck8l4

React intl template string throws a missed id warning

I'm trying to inject some values into translated sentence via react-intl.
Following official documentation I was able to define some template message into my Localization file and it looks like that.
ratingMsgTemplate: {
id: "_ratingMsg",
defaultMessage: "{pointCount, plural, one {{point}} other {{points}}}"
},
this field passed directly inside localization object.
than I'm using a custom Plural component which is very simple
import * as React from "react";
import { injectIntl } from "react-intl";
import { inject, observer } from "mobx-react";
const i18nPluralNumber = (props: any) => {
const { locale, intl, msgId, ...msgParams } = props;
let finalMessage;
if (!(msgId in locale.messages)) {
console.warn("Id not found in i18n messages list: " + msgId);
finalMessage = msgId;
} else {
finalMessage = locale.messages[msgId];
}
return (
<span className="plural-number-intl">
{intl.formatMessage(finalMessage, { ...msgParams })}
</span>
);
};
export default inject("locale")(injectIntl(observer(i18nPluralNumber)));
here is the example of use
<I18nPluralNumber
msgId="ratingMsgTemplate"
pointCount={shopPoints}
point={formatMessage("point")}
points={formatMessage("points")}
/>
It works like a charm except this pesky thing.
In console I'm receiving this message:
I18nPluralNumber.tsx:19 [React Intl] Missing message: "_ratingMsg" for locale: "de", using default message as fallback.
and it's correct because there is no any _ratingMsg id inside my translate file. Actually I added this id only because it's necessary following react-intl docs and without this ID it isn't working at all
Could anyone give some tip/advice how to manage this stuff?
I'll be appreciated for any info.
If it helps anyone. I didn't found any normal solution except this crutch.
I've added the id with this key and duplicated the defaultMessage for each of it.
eg
ratingMsgTemplate: {
id: "_ratingMsg",
defaultMessage: "{pointCount, plural, one {{point}} other {{points}}}"
},
I just create the id - _ratingMsg with the defaultMessage value
_ratingMsg: "{pointCount, plural, one {{point}} other {{points}}}",
ratingMsgTemplate: {
id: "_ratingMsg",
defaultMessage: "{pointCount, plural, one {{point}} other {{points}}}"
},
now it doesn't throws warning about missed ID and it works like a template string.
Weird but I didn'd find any better solution

React JS: Loop through a multi-dimensional array and determine/load the correct component

Using React, I'm fetching data from an API, a sample of which can be seen here.
I need to loop through the body section (multi-dimensional array) and then determine what type of 'block' it is:
heading
text
quote
frameImgeBlock
quoteImageBlock
frameQuoteBlock
Depending which block type it is then I need to create/load the corresponding React component (as I'm imagining it's better to separate these into individual React components).
How is it best to approach this in React/JS?
I may be able to tweak the API output a little if someone can suggest an easier approach there.
You could use a "translation" function like so, which would call external components:
// Import external deps
import Heading from './src/heading';
// Later in your component code
function firstKey(obj) {
return Object.keys(obj)[0];
}
getDomItem(item) {
const key = firstKey(item);
const val = item[val];
switch (key) {
case "heading":
return <Heading heading={ val.heading } ...etc... />
case "other key..."
return <OtherElm ...props... />
}
}
render() {
// data is your object
return data.content.body.map(item =>
this.getDomItem(item[firstKey(item)])
);
}

Resources