How to get locale for localeCompare from i18next? - javascript

Our project is using i18next for translations, and I'm currently needing to sort strings using String.prototype.localeCompare
How would I go about getting a code like 'en', 'fr', 'en-US', etc from i18next? Does i18next default to a specific code? Would I need to use an i18next instance or the default export?
I believe these might be called "BCP 47 language tags"
Thanks

Ok, so the brief answer is that you just use i18next.language
In our case, it did matter that we used the correct instance, not just the default import. We also used an additional layer called react-i18next as shown below.
import { I18nContext } from "react-i18next";
...
const { i18n } = useContext(I18nContext);
...
return isLocaleCompareSupported()
? aText.localeCompare(bText, i18n.language)
: aText.localeCompare(bText);

Related

How can I build a Typescript string literal type with an imported array?

In one of my config files config.js file, I have:
module.exports = {
locales: ['en', 'fr']
}
In my library, I try to import the config.js file and turn it into a typescript liberal type like so:
import config from "config.js"
const tempDefaultLocales = [...config.locales] as const
export type Language = typeof tempDefaultLocales[number]
But the type of Language is string and not "en" | "fr"
If I don't import and just hard type it, the as const works. Example:
const locales = ["en","fr"] as const
type Language = typeof locales[number]
Gives me the correct type of "en" | "fr"
Any idea how I can make this work without changing my config file to ts or hardcoding it?
Here is a sandbox: https://codesandbox.io/s/awesome-swirles-ij4qsg?file=/src/App.tsx
PS: I can't change the config to ts because the config file is actually next.config.js and they don't allow us to change it and I want to avoid having 2 config files to set my languages.
Thanks
Adding this as a new answer as it's a different approach.
Try to markup the exported JS with the correct JSDoc annotations, which should be read by TS:
module.exports = /** #type {const} */ ({
locales: ['en', 'fr']
})
This was supported since 4.5. See here. Working codesandbox.
Define the exported object as a const. Working example here.
module.exports = {
locales: ['en', 'fr']
} as const
as const does not "propogate". You cant define another variable somewhere else that isn't a const and then assume it will treat it as one because the as const guarantees are missing on the source data, so typescript can't guarantee it won't change or be added to by some other code.

i18next with namespace does not fetch expected translation file

I'm trying to setup localisation for an existing app. I've gotten fairly far. But something is not quite right.
My setup is having multiple translation calls in the files like so: {attribute: 'stress', text: t('dass21::I found it hard to wind down')},
As you can see, I changed the namespace separator to be a double :: since I have a lot of medical specific sentences which are hard to think keys for, so the actual sentence is the key.
To export the translation into json translations files I use: i18next 'src/**/*.js' which works fine. It makes a folder for every language, and in that folder, for every namespace it makes a file. (Eg in this case, it will make a file dass21.json).
My configuration for the i18next-parser is:
module.exports = {
// Since keys hold regular english I can't have the default settings
namespaceSeparator: '::',
keySeparator: '_',
pluralSeparator: "|",
locales: ['en', 'es'],
output: 'assets/locales/$LOCALE/$NAMESPACE.json',
}
I matches those settings in the i18next init like so:
import i18next from "i18next";
import HttpApi from 'i18next-http-backend';
i18next
.use(HttpApi)
.init({
backend: {
// for all available options read the backend's repository readme file
loadPath: '/assessments/locales/{{lng}}/{{ns}}.json',
},
fallbackLng: 'en',
lng: 'en',
supportedLngs: ['en', 'es'],
namespaceSeparator: '::',
keySeparator: '_',
pluralSeparator: "|",
});
const t = i18next.t
export default t
As you can see, I want to load my translation files over http (which are served by a node server and this works). However, it only tries to load a generic translation file called translation.js and none of my specific namespace files. Even though the call to t with dass21::I found it hard to wind down is called. (Since it is visible on the screen using its key).
How can I make sure it also tries to load the namespace specific files over http, while having the custom namespace separators?
If you're using i18next, make sure you pass in the desired namespace in to the useTranslation or withTranslation function: https://react.i18next.com/guides/multiple-translation-files
Alternatively, use the ns init option and define your namespaces.

Import dynamically named exports

Is it possible to import named exports dynamically?
I have a file, banana.js with hundreds of named exports. Id like to import them on demand. Is this possible? And if it is, will it only load that export and not all?
I know its possible to import them dynamically from individual files but I want them in the same file.
Example below..
// banana.js
export const banana_1 = {
..
}
export const banana_2 = {
..
}
// main.js
const currentPage = 1
async getSomething(){
let { `banana_${currentPage}` } = await import('./banana.js');
const foo = `banana_${currentPage}`
}
Fyi im using Vue.js
From what I know, you might have to use require('./banana.js') here. Please note that require is synchronous, so need for await. If you use eslint and it forbids usage of require, just add // eslint-disable-line to the end of that line. BTW, I don't know Vue at all.
About the object you import, you should probably make an array instead of suffixing each export with a number.
Edit: I just realized that dynamic imports are a thing not only possible with require, so ignore the first paragraph.
Based on your response to my question I offer the following solution. I understand not wanting to deploy and use a full database solution for the sake of hot loading some JSON objects. I'm unsure of your use case and other details related to your project.
You can use a self contained database like SQLite. Instead of setting up a NoSQL or SQL server. You can instead just store what you need to the SQLite file. Since Vue requires Node.js you can use the Node.js SQLite3 library https://www.npmjs.com/package/sqlite3.
I hope this helps.

Clever way to define a GraphQL Query/Mutation with JavaScript

Maybe it's just me but I´m pretty much annoyed of defining my GraphQL Queries/Mutations with backticks. The problem here is that, if I have to apply some changes to the query string, it's pretty annoying to reformat the string inside the backticks.
Is there a clever way to define a more readable and maintainable GraphQL Query/Mutation with JavaScript? It's also hard to find missing brackets when the query string is messed up.
I am doing like this right now:
import gql from 'graphql-tag';
export const fancyMutation = gql`
mutation($id: ID!)
{
delete_something(id:$id) {
id
foo
bar
}
}
`;
You can also put your queries into their own files, e.g. with a .graphql or .gql file extension, and use the graphql-tag/loader to load the query from the file where you need it.
Given this query in its own file:
query CurrentUserForLayout {
currentUser {
login
avatar_url
}
}
With webpack you would need the following configuration:
module: {
rules: [
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
},
],
},
Then you could load it like that:
import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import currentUserQuery from './currentUser.graphql';
class Profile extends Component { ... }
Profile.propTypes = { ... };
export default graphql(currentUserQuery)(Profile)
Update:
You can also have multiple queries in one file:
query MyQuery1 {
...
}
query MyQuery2 {
...
}
You can then load them like this:
import { MyQuery1, MyQuery2 } from 'query.gql'
Other than that I don't know about other options to define queries inline. The backticks aren't graphql specific. It just uses the es6 template tags like e.g. styled components does to define inline css. Your only option is to get an IDE that can recognize graphql queries to provide editing help and code highlighting. IntelliJ Editors (IDEA, WebStorm, PyCharm) have a plugin for that like #Victor Vlasenko pointed out.
JS GraphQL WebStorm plugin starting from version 1.4.1 supports embedded GraphQL strings.
From the WebStorm menu select File -> Settings -> Plugins -> Browse repositories and find JS GraphQL plugin. Check that you have at least version 1.4.1 of this plugin installed.
When installed it should highlight braces and let you reformat GraphQL inside string by selecting code block and choosing Code -> Reformat code from IDE menu.
This plugin project page is located here:
https://github.com/jimkyndemeyer/js-graphql-intellij-plugin

"No locale data has been provided" regardless of what is passed in

I am trying to use intl to do some formatting but no matter what I pass in as the locale, I always get the following error message:
ReferenceError: No locale data has been provided for this object yet
I have tried the following:
new Intl.NumberFormat('en-ZA', { minimumFractionDigits: percentDecimals });
as well as
new Intl.NumberFormat(['en-ZA'], { minimumFractionDigits: percentDecimals });
and I am not sure what else do.
I have added the package to the package.json
"intl": "latest"
and I do import it
import Intl from "intl";
Depending on the enviromnent you are running this code you might need to import locale data as well to polyfill locale
import 'intl/locale-data/jsonp/en-ZA'
This import does side-effect that register en-ZA locale IntlPolyfill.__addLocaleData({locale:"en-ZA", when polyfill is required.
For me it was solved by importing the "intl" at the top of the source code of the app.
In my case I was doing the import in (i18n.ts).
(i18n.ts) is where i18next is initialized. Then I have to move the import to the App.tsx.
// Polyfill Intl as it is not included in RN
import "intl";

Categories

Resources