I'm using i18next to handle translations, with i18next-node-fs-backend, to load translations from a filepath. My i18n.init() function looks like this:
i18next.use(i18nextBackend)
.init({
lng: 'en',
ns: ['module1, module2],
backend: {
loadPath: rootFolder + '/node_modules/{{ns}}/locales/{{lng}}.json',
}
}...
What i want to do is load all translations from that loadPath AND also load the translations from another file, located in another path, like rootFolder + '/locales/{{lng}}.json', like passing to path to loadPath parameter
loadPath: [rootFolder + '/node_modules/{{ns}}/locales/{{lng}}.json', rootFolder + '/locales/{{lng}}.json']
Is it possible to do that? Any suggestions?
Thanks.!
/**
* Utility function to determine loadpath for a given language and namespace
* this allows us to separate local files by namespace which makes it easier to
* find translations and manage them.
*
* #param {*} lng the language locale
* #param {*} namespace the namespace name as specified in the i18n 'ns' config
*/
function loadPath(lng, namespace) {
// console.log('loadPath', lng, namespace);
let path = `/locales/common/${lng}/translation.json`;
/**
* Add additional case stmts for new locale sub directories.
* This allows for splitting up translation files into namespaces, namespace can
* then be attached to a specific component or accessed through notation.
*/
switch (namespace[0]) {
case 'common':
path = `/locales/common/${lng}/translation.json`;
break;
case 'container':
path = `/locales/container/${lng}/translation.json`;
break;
default:
break;
}
// console.log('loadPath', path);
return path;
}
then in i18n.js configuration
i18n
.use(Backend) // load translation using xhr -> see /public/locales
.init({
// i18next-xhr-backend config for loading files from different locations
backend: {
loadPath: loadPath,
},
});
Related
I am trying to get list of version ids for a blob using node.js.
async function getBlobNameAndVersions() {
for await (const blob of containerClient.listBlobsFlat(includeVersions?: true)) {
const tempBlockBlobClient = containerClient.getBlockBlobClient(blob.name);
console.log(`\n\tname: ${blob.versionId}\n\tURL: ${tempBlockBlobClient.name}\n`);
}
}
getBlobNameAndVersions()
Received error saying "ReferenceError: inlcudeVersions is not defined"
When looked at the reference listBlobsFlat, below were options to include versions.
listBlobsFlat(options?: ContainerListBlobsOptions): PagedAsyncIterableIterator<BlobItem, ContainerListBlobFlatSegmentResponse>;
/**
* Returns an AsyncIterableIterator for ContainerListBlobHierarchySegmentResponse
*
* #param delimiter - The character or string used to define the virtual hierarchy
* #param marker - A string value that identifies the portion of
* the list of blobs to be returned with the next listing operation. The
* operation returns the ContinuationToken value within the response body if the
* listing operation did not return all blobs remaining to be listed
* with the current page. The ContinuationToken value can be used as the value for
* the marker parameter in a subsequent call to request the next page of list
* items. The marker value is opaque to the client.
* #param options - Options to list blobs operation.
Tried using options as below.
export declare interface ContainerListBlobsOptions extends CommonOptions {
* Specifies whether versions should be included in the enumeration. Versions are listed from oldest to newest in the response.
*/
includeVersions?: boolean;
}
You would need to pass the options as an object i.e. instead of passing includeVersions?: true, you would need to use {includeVersions: true}.
So your code would be something like:
async function getBlobNameAndVersions() {
for await (const blob of containerClient.listBlobsFlat({includeVersions: true})) {
const tempBlockBlobClient = containerClient.getBlockBlobClient(blob.name);
console.log(`\n\tname: ${blob.name}\n\tURL: ${tempBlockBlobClient.url}\n`);
}
}
Updated Code is working to get version ids of blobs and its URL's
async function getBlobNameAndVersions() {
for await (const blob of containerClient.listBlobsFlat({includeVersions: true})){
const tempBlockBlobClient = containerClient.getBlockBlobClient(blob.name);
console.log(`\n\tversion: ${blob.versionId}\n\tURL: ${tempBlockBlobClient.url}\n`);
}
}
I want to reference a variable to a plain object from another file via JSDoc to use IDE autocomplete.
Here is my example:
MySample.js
export const MySample = {
findMe() {
return "Hi!";
},
};
export default MySample;
index.js
import MySample from "./MySample.js";
let window = {};
function inject($key, $object) {
window[$key] = $object;
}
inject("MySample", MySample);
let my_sample = window.MySample;
console.log(my_sample.findMe());
I tried to use something like the following, but it was not successful:
/**
* #type {import('./MySample').MySample} my_sample
*/
P.S: I know if My Sample be a class, the above solution is working. But in my scenario I want to reference a plain object.
I found the solution as follows:
/**
* #param {import('./MySample').MySample} my_sample
*/
inject("MySample", MySample);
The key is using #param instead of #type
Is there any way to customize the Nav Bar or the Header to have a custom link?
The use-case is that I have a JIRA issue collector that is driven by javascript. I would like the user to provide feedback from the page they are having issues. However, any solution I can come up with so far takes the user away from the current page.
Example of what I have that takes the user away:
I currently have a Suitelet that is in one of the menus. That Suitelet invokes javascript but even then the user is taken away.
I have a workflow on the case record that calls some Javascript Javascript in one of the UI-based action's conditions is invoked. Similar to #1 but on the case record.
I'm thinking I'm going to need to create and public a chrome extension for my company's domain just to get a pervasive bit of javascript to run for all pages...seems like a sledgehammer.
I hope someone can prove me wrong, but as far as I am aware there is no way to natively inject Javascript or anything into the NetSuite header/navbar - they don't offer customisation to the header/navbar.
I've resorted to creating a Userscript that I load through the Violent Monkey extension for Chrome or Firefox.
Example Userscript Template
// ==UserScript==
// #name NetSuite Mods (Example)
// #namespace Violentmonkey Scripts
// #match *.netsuite.com/*
// #include *.netsuite.com/*
// #grant GM_addStyle
// #version 1.0
// #author Kane Shaw - https://stackoverflow.com/users/4561907/kane-shaw
// #description 6/11/2020, 6:25:20 PM
// ==/UserScript==
// Get access to some commonly used NLAPI functions without having to use "unsafeWindow.nlapi..." in our code
// You can add more of these if you need access to more of the functions contained on the NetSuite page
nlapiSetFieldText = unsafeWindow.nlapiSetFieldText;
nlapiSetFieldValue = unsafeWindow.nlapiSetFieldValue;
nlapiGetFieldText = unsafeWindow.nlapiGetFieldText;
nlapiGetFieldValue = unsafeWindow.nlapiGetFieldValue;
nlapiSearchRecord = unsafeWindow.nlapiSearchRecord;
nlobjSearchFilter = unsafeWindow.nlobjSearchFilter;
nlapiLookupField = unsafeWindow.nlapiLookupField;
nlapiLoadRecord = unsafeWindow.nlapiLoadRecord;
nlapiSubmitRecord = unsafeWindow.nlapiSubmitRecord;
GM_pageTransformations = {};
/**
* The entrypoint for our userscript
*/
function GM_main(jQuery) {
// We want to execute these on every NetSuite page
GM_pageTransformations.header();
GM_pageTransformations.browsertitle();
// Here we build a function name from the path (page being accessed on the NetSuite domain)
var path = location.pathname;
if(path.indexOf('.')>-1) path = path.substr(0,path.indexOf('.'));
path = toCamelCase(path,'/');
// Now we check if a page "GM_pageTransformations" function exists with a matching name
if(GM_pageTransformations[path]) {
console.log('Executing GM_pageTransformations for '+path);
GM_pageTransformations[path]();
} else {
console.log('No GM_pageTransformations for '+path);
}
}
/**
* Changes the header on all pages
*/
GM_pageTransformations['header'] = function() {
// For example, lets make the header background red
GM_addStyle('#ns_header, #ns_header * { background: red !important; }');
}
/**
* Provides useful browser/tab titles for each NetSuite page
*/
GM_pageTransformations['browsertitle'] = function() {
var title = jQuery('.uir-page-title-secondline').text().trim();
var title2 = jQuery('.uir-page-title-firstline').text().trim();
var title3 = jQuery('.ns-dashboard-detail-name').text().trim();
if(title != '') {
document.title = title+(title2 ? ': '+title2 : '')+(title3 ? ': '+title3 : '');
} else if(title2 != '') {
document.title = title2+(title3 ? ': '+title3 : '');
} else if(title3 != '') {
document.title = title3;
}
}
/**
* Changes app center card pages (dashboard pages)
*/
GM_pageTransformations['appCenterCard'] = function() {
// For example, lets make add a new heading text on all Dashboard pages
jQuery('#ns-dashboard-page').prepend('<h1>My New Dashboard Title</h1>');
}
/**
* Convert a given string into camelCase, or CamelCase
* #param {String} string - The input stirng
* #param {String} delimter - The delimiter that seperates the words in the input string (default " ")
* #param {Boolean} capitalizeFirstWord - Wheater or not to capitalize the first word (default false)
*/
function toCamelCase(string, delimiter, capitalizeFirstWord) {
if(!delimiter) delimiter = ' ';
var pieces = string.split(delimiter);
string = '';
for (var i=0; i<pieces.length; i++) {
if(pieces[i].length == 0) continue;
string += pieces[i].charAt(0).toUpperCase() + pieces[i].slice(1);
}
if(!capitalizeFirstWord) string= string.charAt(0).toLowerCase()+string.slice(1);
return string;
}
// ===============
// CREDIT FOR JQUERY INCLUSION CODE: Brock Adams # https://stackoverflow.com/a/12751531/4561907
/**
* Check if we already have a local copy of jQuery, or if we need to fetch it from a 3rd-party server
*/
if (typeof GM_info !== "undefined") {
console.log("Running with local copy of jQuery!");
GM_main(jQuery);
}
else {
console.log ("fetching jQuery from some 3rd-party server.");
add_jQuery(GM_main, "1.9.0");
}
/**
* Add the jQuery into our page for our userscript to use
*/
function add_jQuery(callbackFn, jqVersion) {
var jqVersion = jqVersion || "1.9.0";
var D = document;
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
var scriptNode = D.createElement ('script');
scriptNode.src = 'https://ajax.googleapis.com/ajax/libs/jquery/'
+ jqVersion
+ '/jquery.min.js'
;
scriptNode.addEventListener ("load", function () {
var scriptNode = D.createElement ("script");
scriptNode.textContent =
'var gm_jQuery = jQuery.noConflict (true);\n'
+ '(' + callbackFn.toString () + ')(gm_jQuery);'
;
targ.appendChild (scriptNode);
}, false);
targ.appendChild (scriptNode);
}
You can copy and paste that code as-is into a new Userscript and it will do the following:
Make Browser tabs/windows have useful titles (shows order numbers, customer names, vendor names etc - not just "Sales Order")
Change the header background to red (as an example)
Add a new heading to the top of all "Dashboard" pages that says "My New Dashboard Title" (as an example)
Can I allow the domain matching for my extension to be user configurable?
I'd like to let my users choose when the extension runs.
To implement customizable "match patterns" for content scripts, the Content script need to be executed in by the background page using the chrome.tabs.executeScript method (after detecting a page load using the chrome.tabs.onUpdated event listener).
Because the match pattern check is not exposed in any API, you have to create the method yourself. It is implemented in url_pattern.cc, and the specification is available at match patterns.
Here's an example of a parser:
/**
* #param String input A match pattern
* #returns null if input is invalid
* #returns String to be passed to the RegExp constructor */
function parse_match_pattern(input) {
if (typeof input !== 'string') return null;
var match_pattern = '(?:^'
, regEscape = function(s) {return s.replace(/[[^$.|?*+(){}\\]/g, '\\$&');}
, result = /^(\*|https?|file|ftp|chrome-extension):\/\//.exec(input);
// Parse scheme
if (!result) return null;
input = input.substr(result[0].length);
match_pattern += result[1] === '*' ? 'https?://' : result[1] + '://';
// Parse host if scheme is not `file`
if (result[1] !== 'file') {
if (!(result = /^(?:\*|(\*\.)?([^\/*]+))(?=\/)/.exec(input))) return null;
input = input.substr(result[0].length);
if (result[0] === '*') { // host is '*'
match_pattern += '[^/]+';
} else {
if (result[1]) { // Subdomain wildcard exists
match_pattern += '(?:[^/]+\\.)?';
}
// Append host (escape special regex characters)
match_pattern += regEscape(result[2]);
}
}
// Add remainder (path)
match_pattern += input.split('*').map(regEscape).join('.*');
match_pattern += '$)';
return match_pattern;
}
Example: Run content script on pages which match the pattern
In the example below, the array is hard-coded. In practice, you would store the match patterns in an array using localStorage or chrome.storage.
// Example: Parse a list of match patterns:
var patterns = ['*://*/*', '*exampleofinvalid*', 'file://*'];
// Parse list and filter(exclude) invalid match patterns
var parsed = patterns.map(parse_match_pattern)
.filter(function(pattern){return pattern !== null});
// Create pattern for validation:
var pattern = new RegExp(parsed.join('|'));
// Example of filtering:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status === 'complete') {
var url = tab.url.split('#')[0]; // Exclude URL fragments
if (pattern.test(url)) {
chrome.tabs.executeScript(tabId, {
file: 'contentscript.js'
// or: code: '<JavaScript code here>'
// Other valid options: allFrames, runAt
});
}
}
});
To get this to work, you need to request the following permissions in the manifest file:
"tabs" - To enable the necessary tabs API.
"<all_urls>" - To be able to use chrome.tabs.executeScript to execute a content script in a specific page.
A fixed list of permissions
If the set of match patterns is fixed (ie. the user cannot define new ones, only toggle patterns), "<all_urls>" can be replaced with this set of permissions. You may even use optional permissions to reduce the initial number of requested permissions (clearly explained in the documentation of chrome.permissions).
What i want to do is, to detect the language of a users browser and redirect him to a page containing the locale in url.
I thought, the easiest way would be, to register a kernel listener. So that is what i did:
services:
kernel__request_listener:
class: Me\MainBundle\Listener\KernelRequestListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
calls:
- [ setContainer, [ #service_container ] ]
KernelRequestListener.php
...
/**
* #Service
*/
class KernelRequestListener
{
/**
* #Observe("kernel.request")
*/
public function onKernelRequest( GetResponseEvent $response )
{
if( $newLocale = $this->newLocale() )
{
$parmArray = $request->get('_route_params');
$parmArray['_locale'] = $newLocale;
$redirectResponse = new RedirectResponse( $this->getContainer()->get('router')->generate($request->get('_route'), $parmArray) );
$redirectResponse->headers->setCookie( new Cookie('b_locale', $newLocale, time() + 2592000) );
$response->setResponse( $redirectResponse );
}
}
...
}
The method $this->newLocale() just detects if the user should be redirected to another language and returns the new language code (i.e. DE or FR).
Here comes the problem:
I am using assetics to compress the js files and jms/i18n-routing-bundle to do the locale-based routing. When the kernel listener switches locale, the page starts loading the js files over and over again. also, there are several pages (i.e. the profiler, login/logout etc.) where no redirect should take place as it makes no sense or breaks smthing.
Is a kernel listener the right place to do such a redirection or is there any better place. How to resolve the problems above?
Add, before your code:
public function onKernelRequest(GetResponseEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
$request = $event->getRequest();
if ($request->getRequestFormat() !== 'html') {
return;
}
[CODE]