We've run into some problems for our MultiSite Spartacus setup when doing I18n.
We'd like to have different translations for each site, so we put these on an API that can give back the messages dependent on the baseSite, eg: backend.org/baseSiteX/messages?group=common
But the Spartacus setup doesn't let us pass the baseSite? We can
pass {{lng}} and {{ns}}, but no baseSite.
See https://sap.github.io/spartacus-docs/i18n/#lazy-loading
We'd could do it by overriding i18nextInit, but I'm unsure how to achieve this.
In the documentation, it says you can use crossOrigin: true in the config, but that does not seem to work. The type-checking say it's unsupported, and it still shows uw CORS-issues
Does someone have ideas for these problems?
Currently only language {{lng}} and chunk name {{ns}} are supported as dynamic params in the i18n.backend.loadPath config.
To achieve your goal, you can implement a custom Spartacus CONFIG_INITIALIZER to will populate your i18n.backend.loadPath config based on the value from the BaseSiteService.getActive():
#Injectable({ providedIn: 'root' })
export class I18nBackendPathConfigInitializer implements ConfigInitializer {
readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
readonly configFactory = () => this.resolveConfig().toPromise();
constructor(protected baseSiteService: BaseSiteService) {}
protected resolveConfig(): Observable<I18nConfig> {
return this.baseSiteService.getActive().pipe(
take(1),
map((baseSite) => ({
i18n: {
backend: {
// initialize your i18n backend path using the basesite value:
loadPath: `https://backend.org/${baseSite}/messages?lang={{lng}}&group={{ns}}`,
},
},
}))
);
}
}
and provide it in your module (i.e. in app.module):
#NgModule({
providers: [
{
provide: CONFIG_INITIALIZER,
useExisting: I18nBackendPathConfigInitializer,
multi: true,
},
],
/* ... */
})
Note: the above solution assumes the active basesite is set only once, on app start (which is the case in Spartacus by default).
Related
In app.module.ts I have the following:
#Module({
imports: [
...,
GraphQLModule.forRoot<ApolloGatewayDriverConfig>({
server: {
context: getContext,
},
driver: ApolloGatewayDriver,
gateway: {
buildService: ({ name, url }) => {
return new RemoteGraphQLDataSource({
url,
willSendRequest({ request, context }: any) {
...
},
});
},
supergraphSdl: new IntrospectAndCompose({
subgraphs: [
{ name: 'iam', url: API_URL_IAM },
],
})
},
}),
]
...
})
here getContext is just a regular function which is not part of nestjs context (doesn't have injection, module capability) like below:
export const getContext = async ({ req }) => {
return {}
}
Is there any way to use nestjs services instead of plain old functional approach to build the context for graphql gateway in nestjs?
Thanks in advance for any kind of help.
I believe you're looking to create a service that is #Injectable and you can use that injectable service via a provider. What a provider will do is satisfy any dependency injection necessary.
In your scenario, I would import other modules as necessary. For building context, I would create a config file to create from env variables. Then create a custom provider that reads from the env variables and provides that implementation of the class/service to the other classes as their dependency injection.
For example, if I have a graphQL module. I would import the independent module. Then, I would provide in the providers section, the handler/service classes and the dependencies as an #injectable. Once your service class is created based on your config (which your provider class would handle), you would attach that service class to your GraphQL class to maybe lets say direct the URL to your dev/prod envs.
I deployed my nextjs app to Vercel and it was a success.
I can see a preview of the website and even the log saying it works well.
But i try to access the website via the default Vercel domain :
https://tly-nextjs.vercel.app/
I get an ERR_TOO_MANY_REDIRECTS.
I do not have this problem locally.
I tried :
Disabling a language redirect that I use (/en for english folks, and / for french people).
Disabling the language detector of i18next.
But none of these solutions changed anything.
Any ideas ?
i18n.js file
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Cache from 'i18next-localstorage-cache';
import LanguageDetector from 'i18next-browser-languagedetector';
const fallbackLng = ['fr'];
const availableLanguages = ['fr', 'en'];
const en = require('./locales/en/common.json');
const fr = require('./locales/fr/common.json');
const options = {
order: ['querystring', 'navigator'],
lookupQuerystring: 'lng'
}
const cacheOptions = {
// turn on or off
enabled: true,
// prefix for stored languages
prefix: 'i18next_res_',
// expiration
expirationTime: 365*24*60*60*1000,
// language versions
//versions: {}
}
i18n
.use(Cache)
.use(initReactI18next)
.use(LanguageDetector)
.init({
cache: cacheOptions,
fallbackLng: fallbackLng,
debug: true,
detection: options,
supportedLngs: availableLanguages,
nonExplicitSupportedLngs: true,
resources: {
en: {translation: en},
fr: {translation: fr},
},
interpolation: {
escapeValue: false,
},
react: {
wait: true,
useSuspense: true,
},
});
export default i18n;
My change Language function :
const changeLanguageInHouse = (lang, bool) => {
setLoading(true);
i18next.changeLanguage(lang).then((t) => {
setLanguage(lang);
bake_cookie("langChoice", lang);
setLoading(false);
if (bool === true) {
var newUrl2 = (lang === "fr" ? "" : "/en") + asPath;
window.location.replace(newUrl2);
}
});
};
What happend at your server is following:
You enter https://tly-nextjs.vercel.app/ and it is redirected to /en with HTTP-Status-Code 307 (Temporary Redirect).
And /en redirect with 301 (Permanent Redirect) to /.
You can reproduce this by open the Browser-Dev-Tools and have a look at the Network Tab.
It might be, that you have some kind of url-rewriting activated at your server, which redirect everything to your domain-root.
Is there a public repo available for this? Here is how it worked for me.
Try changing the order of the locales and the default locale (not sure this helps, but it changed something for me. Undocumented if that is the case!)
So I put the default locale first (which is nl for me) in both the locales array and the domains array.
Let me know if that helps!
module.exports = {
i18n: {
localeDetection: false,
// These are all the locales you want to support in
// your application
locales: ['nl', 'en'],
// This is the default locale you want to be used when visiting
// a non-locale prefixed path e.g. `/hello`
defaultLocale: 'nl',
// This is a list of locale domains and the default locale they
// should handle (these are only required when setting up domain routing)
domains: [
{
domain: 'example.be',
defaultLocale: 'nl',
},
{
domain: 'example.com',
defaultLocale: 'en',
},
],
},
trailingSlash: true,
};
I changed all my getInitialProps to getServerSideProps
and realised I was doing a redirect on response :
res.writeHead(301, { Location: "/" })
I just delete it.
And now I don't have this endless redirect.
Doing this worked for me...
https://ardasevinc.tech/cloudflare-vercel-too-many-redirects
I think it's the actual solution to the cause of the problem rather than a bandage!
Webpack has a resolve.mainFields configuration: https://webpack.js.org/configuration/resolve/#resolvemainfields
This allows control over what package.json field should be used as an entrypoint.
I have an app that pulls in dozens of different 3rd party packages. The use case is that I want to specify what field to use depending on the name of the package. Example:
For package foo use the main field in node_modules/foo/package.json
For package bar use the module field in node_modules/bar/package.json
Certain packages I'm relying on are not bundled in a correct manner, the code that the module field is pointing to does not follow these rules: https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md This causes the app to break if I wholesale change the webpack configuration to:
resolve: {
mainFields: ['module']
}
The mainFields has to be set to main to currently get the app to work. This causes it to always pull in the CommonJS version of every dependency and miss out on treeshaking. Hoping to do something like this:
resolve: {
foo: {
mainFields: ['main']
},
bar: {
mainFields: ['module'],
}
Package foo gets bundled into my app via its main field and package bar gets bundled in via its module field. I realize the benefits of treeshaking with the bar package, and I don't break the app with foo package (has a module field that is not proper module syntax).
One way to achieve this would be instead of using resolve.mainFields you can make use of resolve.plugins option and write your own custom resolver see https://stackoverflow.com/a/29859165/6455628 because by using your custom resolver you can programmatically resolve different path for different modules
I am copy pasting the Ricardo Stuven's Answer here
Yes, it's possible. To avoid ambiguity and for easier implementation,
we'll use a prefix hash symbol as marker of your convention:
require("#./components/SettingsPanel");
Then add this to your configuration file (of course, you can refactor
it later):
var webpack = require('webpack');
var path = require('path');
var MyConventionResolver = {
apply: function(resolver) {
resolver.plugin('module', function(request, callback) {
if (request.request[0] === '#') {
var req = request.request.substr(1);
var obj = {
path: request.path,
request: req + '/' + path.basename(req) + '.js',
query: request.query,
directory: request.directory
};
this.doResolve(['file'], obj, callback);
}
else {
callback();
}
});
}
};
module.exports = {
resolve: {
plugins: [
MyConventionResolver
]
}
// ...
};
resolve.mainFields not work in my case, but resolve.aliasFields works.
More details in https://stackoverflow.com/a/71555568/7534433
I'm upgrading to Angular 6 using AngularFire2. My app referenced 2 Firebase projects using code like this to create the database reference:
public initFirebaseApp(config: FirebaseAppConfig, firebaseAppName: string) {
this._db = new AngularFireDatabase(_firebaseAppFactory(config, firebaseAppName));
}
This code is now broken. I get this:
ERROR in src/app/services/firebase.service.ts(24,25): error TS2554: Expected 5 arguments, but got 1.
Thanks!
AngularFire now support many more configuration objects via Injection now, which is why it's expecting more arguments. It currently takes:
constructor(
#Inject(FirebaseOptionsToken) options:FirebaseOptions,
#Optional() #Inject(FirebaseNameOrConfigToken) nameOrConfig:string|FirebaseAppConfig|undefined,
#Optional() #Inject(RealtimeDatabaseURL) databaseURL:string,
#Inject(PLATFORM_ID) platformId: Object,
zone: NgZone
)
Though now that we support dependency injection, I wouldn't suggest directly initializing like you are to support multiple apps. We have an open issue for documenting this but you can now inject different FirebaseOptions via the FirebaseOptionsToken into different components, if you need multiple databases in the same component use something like this:
#Injectable()
export class AngularFireDatabaseAlpha extends AngularFireDatabase { }
#Injectable()
export class AngularFireDatabaseBeta extends AngularFireDatabase { }
export function AngularFireDatabaseAlphaFactory(platformId: Object, zone: NgZone): Project1AngularFireAuth {
return new AngularFireDatabaseAlpha(environment.firebase[0], 'alpha', undefined, platformId, zone)
}
export function AngularFireDatabaseBetaFactory(platformId: Object, zone: NgZone): Project2AngularFireAuth {
return new AngularFireDatabaseBeta(environment.firebase[1], 'beta', undefined, platformId, zone)
}
#NgModule({
...,
providers: [
...,
{ provide: AngularFireDatabaseAlpha, deps: [PLATFORM_ID, NgZone], useFactory: AngularFireDatabaseAlphaFactory },
{ provide: AngularFireDatabaseBeta, deps: [PLATFORM_ID, NgZone], useFactory: AngularFireDatabaseBetaFactory },
...
],
...
})
Then you can just rely on Dependency Injection to get AngularFireDatabaseAlpha and AngularFireDatabaseBeta into your component.
I'm using DocPad to generate system documentation. I am including release notes in the format
http://example.com/releases/1.0
http://example.com/releases/1.1
http://example.com/releases/1.2
http://example.com/releases/1.3
I want to include a link which will redirect to the most recent release.
http://example.com/releases/latest
My question: how do I make a link that will redirect to a relative URL based on configuration? I want this to be easily changeable by a non-programmer.
Update: I've added cleanurls into my docpad.js, similar to example below. (see code below). But using "grunt docpad:generate" seems to skip making the redirect (is this an HTML page?). I've a static site. I also confirmed I'm using the latest cleanurls (2.8.1) in my package.json.
Here's my docpad.js
'use strict';
var releases = require('./releases.json'); // list them as a list, backwards: ["1.3", "1.2", "1.1", "1.0"]
var latestRelease = releases.slice(1,2)[0];
module.exports = {
outPath: 'epicenter/docs/',
templateData: {
site: {
swiftype: {
apiKey: 'XXXX',
resultsUrl: '/epicenter/docs/search.html'
},
ga: 'XXXX'
},
},
collections: {
public: function () {
return this.getCollection('documents').findAll({
relativeOutDirPath: /public.*/, isPage: true
});
}
},
plugins: {
cleanurls: {
simpleRedirects: {'/public/releases/latest': '/public/releases/' + latestRelease}
},
lunr: {
resultsTemplate: 'src/partials/teaser.html.eco',
indexes: {
myIndex: {
collection: 'public',
indexFields: [{
name: 'title',
boost: 10
}, {
name: 'body',
boost: 1
}]
}
}
}
}
};
When I run grunt docpad:generate, my pages get generated, but there is an error near the end:
/data/jenkins/workspace/stage-epicenter-docs/docs/docpad/node_modules/docpad-plugin-cleanurls/node_modules/taskgroup/node_modules/ambi/es6/lib/ambi.js:5
export default function ambi (method, ...args) {
^^^^^^
I can't tell if that's the issue preventing this from running but it seems suspicious.
Providing that your configuration is available to the DocPad Configuration File, you can use the redirect abilities of the cleanurls plugin to accomplish this for both dynamic and static environments.
With a docpad.coffee configuration file, it would look something like this:
releases = require('./releases.json') # ['1.0', '1.1', '1.2', '1.3']
latestRelease = releases.slice(-1)[0]
docpadConfig =
plugins:
cleanurls:
simpleRedirects:
'/releases/latest': '/releases/' + latestRelease
module.exports = docpadConfig