Botbuilder how to use default locale strings in case namespace hasn't defined it - node.js

I am using the botbuilder framework. I have defined several namespaces for the dialogs I have created, such as help or default. For all of these I have also created json files in my locale/en/ directory, and all is well.
However, I have a few sentences that are very common, and I don't feel like copying those over to each of the individual namespaces. I have tried using index.json as a 'fallback' in case the namespace file doesn't define the string. But it doesn't work for me. Contrary to what the documentation seems to suggest.
/locale
/en
/help.json
/default.json
/index.json <-- Doesn't work
/dialogs
/help.js
/default.js
bot.js
Say I have the following library in help.js, that :
lib = new builder.Library('help')
lib.dialog('/', (session) => {
session.send('custom_cancel')
}
module.exports = lib
The library is used in bot.js:
bot.library(require('./dialogs/help'))
And index.json has this content:
{
"custom_cancel": "My custom cancel"
}
Whereas help.json is empty:
{}
Because help.json does not have custom_cancel, the bot will actually send custom_cancel as the string.
Again. I can copy paste the strings to both locations and there is no more problem. But that seems like an ugly solution to me.

I have tried the more explicit version, which seems to help in more cases, but I am not fully convinced yet.
session.localizer.gettext(session.preferredLocale(), 'custom_cancel')
You can use the third argument for the namespace. It seems that '' will point to the index.json file.

Related

No code suggestions for global defined variables in VSCode in a node.js server project

I have to deal with a node.js server project that uses global variables for common APIs. For instance in the entry point server.js there is a Firebase variable for the real-time database that is stored like this:
fireDB = admin.database();
I wasn't aware that this is possible and I would consider this a bad approach, but now I have to deal with it.
I'm not really interested to re-write any of the many calls to this variable in all those files, rather I would find a way to make fireDB show me suggestions only by changing this variable or installing an extension.
I tried to define it on top of the file as var fireDB, but then suggestions only work in the same file, not in others.
When I set a dot behind admin.database() the suggestions work, when I write fireDB. I get no suggestions, yet the call seems to be possible. Suggestions need to work in other files, too. How can I get this to work?
WARNING: MAKE SURE YOU UNDERSTAND THE PROBLEMS WITH GLOBALS BEFORE USING THEM IN A PROJECT
The above warning/disclaimer is mostly for anyone starting a new project that might happen across this answer.
With that out of the way, create a new .d.ts file and put it somewhere with a descriptive name. For example, globals.d.ts at the top level of the directory. Then just populate it with the following (I don't have any experience with firebase, so I had to make some assumptions about which module you're using, etc.):
globals.d.ts
import { database } from "firebase-admin";
declare global {
var fireDB: database.Database;
}
IntelliSense should then recognize fireDB as a global of the appropriate type in the rest of your JavaScript project.
Why does this work? IntelliSense uses TypeScript even if you're working with a JS project. Many popular JS packages includes a .d.ts file where typings are declared, which allows IntelliSense to suggest something useful when you type require('firebase-admin').database(), for example.
IntelliSense will also automatically create typings internally when you do something "obvious", e.g. with literals:
const MY_OBJ = { a: 1, b: "hello"};
MY_OBJ. // IntelliSense can already autocomplete properties "a" and "b" here
Global autocompletion isn't one of those "obvious" things, however, probably because of all the problems with global variables. I'd also guess it'd be difficult to efficiently know what order your files will run in (and hence when a global might be declared). Thus, you need to explicitly declare your global typings.
If you're interested in further augmenting the capabilities of IntelliSense within your JS project, you can also use comments to explicitly create typings:
/**
* #param {String[]} arrayOfStrings
*/
function asAnExample(arrayOfStrings) {
arrayOfStrings. // IntelliSense recognizes this as an array and will provide suggestions for it
}
See this TypeScript JSDoc reference for more on that.

How do I find what files use a specific function or file in a Node project, using VSCode?

I'm working with a Node project using VSCode. I'd like to be able to follow the tree upwards from functions/files at a lower level. That is, if I have an exported function doSomething in file dosomething.js, I'd like to see what code calls this function, and/or see what code requires this file.
// index.js
const { doSomething } = require('./tricky-stuff')
// tricky-stuff.js
const doSomething = function() {}
module.exports = {doSomething}
If I'm browsing tricky-stuff.js, is there a built-in feature to VSCode that allows me to see usages of either the function doSomething or where the file tricky-stuff.js is required/imported, so that it would show me the file index.js in this case (as well as perhaps other files where tricky-stuff is used)? Is there any extension? I seem to recall that WebStorm could do this, but I can't remember. (I know I could search for strings, but that seems inelegant).
I think it largely depends on how much VScode knows about your code, via the TypeScript language service or JSDoc comments etc...
Typically you can right click on a function and select the peek all references or find all references options:
In this case, it will show you all the places where that function is called or referenced.

How to implement localization with dialogs exported as libraries

I have packaged some of my dialogs as libraries by following the instructions from the basics-libraries example. Before, I had all dialogs in app.js and I used localization following the instructions from the documentation. For example, I used
var chatBot = new builder.UniversalBot(chatConnector, {
localizerSettings: {
botLocalePath: "./locale",
defaultLocale: "de"
}
});
and
session.preferredLocale("de");
to use German for all the prompt labels, and stored all the German translations in ./locale/de/index.json and ./locale/de/BotBuilder.json.
However, the localization doesn't work with the dialogs I have packaged into libraries. Instead of the localized strings only the message IDs are displayed.
It works if I use session.localizer.gettext(session.preferredLocale(), "message ID") on each string. However, that is very tedious, and I am wondering if there is a way to localize all strings inside a library at once.
In order to this to work, your locale file should have the same name as your library.
./my_bot_library.js
./locale/en/my_bot_library.json
Then, to get your localized text you can either use:
session.localizer.gettext(session.preferredLocale(), 'message ID', 'my_bot_library')
Please note that the the third parameter is the namespace that
represents the library name
or just the following which internally resolves the preferred Locale and current namespace/library name
session.gettext('message_id') or session.send('message_id')

Common php functions in hack

I decided to start a new project to get into hacklang, and after fixing some if the problems I initially ran into transitioning from php habits, I ran into the following errors:
Unbound name: str_replace
Unbound name: empty
Doing some research I found that this is due to using 'legacy' php which isn't typechecked, and will error with //strict.
That's fine and all, empty() was easy enough to replace, however str_replace() is a bit more difficult.
Is there an equivalent function that will work with //strict? Or at least something similar.
I'm aware that I could use //decl but I feel like that defeats the purpose in my case.
Is there at least any way to tell which functions are implemented in hack and which are not in the documentation as I couldn't find one?
For reference (though it isn't too relevant to the question itself), here is the code:
<?hh //strict
class HackMarkdown {
public function parse(string $content) : string {
if($content===null){
throw new RuntimeException('Empty Content');
}
$prepared = $this->prepare($content);
}
private function prepare(string $contentpre) : Vector<string>{
$contentpre = str_replace(array("\r\n","\r"),"\n",$contentpre);
//probably need more in here
$prepared = Vector::fromArray(explode($contentpre,"\n"));
//and here
return $prepared;
}
}
You don't need to change your code at all. You just need to tell the Hack tools about all the inbuilt PHP functions.
The easiest way to do this is to download this folder and put it somewhere in your project. I put it in a hhi folder in the base of my project. The files in there tell Hack about all the inbuilt PHP functions.
Most of them don't have type hints, which can lead to Hack thinking the return type of everything is mixed instead of the actual return, that is actually correct in most cases as, for example, str_replace can return either a string or a bool. However, it does stop the "unbound name" errors, which is the main reason for adding them.

Autoloading a class in Symfony 2.1

I'm porting a Symfony 1.2 project to Symfony 2.x. I'm currently running the latest 2.1.0-dev release.
From my old project I have a class called Tools which has some simple functions for things like munging arrays into strings and generating slugs from strings. I'd like to use this class in my new project but I'm unclear how to use this class outside of a bundle.
I've looked at various answers here which recommend changing app/autoload.php but my autoload.php looks different to the ones in the answers, maybe something has changed here between 2.0 and 2.1.
I'd like to keep my class in my src or app directories as they're under source control. My vendors directory isn't as I'm using composer to take care of that.
Any advice would be appreciated here.
Another way is to use the /app/config/autoload.php:
<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = require __DIR__.'/../vendor/autoload.php';
$loader->add( 'YOURNAMESPACE', __DIR__.'/../vendor/YOURVENDOR/src' );
// intl
if (!function_exists('intl_get_error_code')) {
require_once _DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs/functions.php';
$loader->add('', __DIR__.'/../vendor/symfony/symfony/src/Symfony/Component/Locale/Resources/stubs');
}
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
return $loader;
Just replace YOURNAMESPACE and YOURVENDOR with your values. Works quite well for me, so far.
You're correct, I stumbled upon the changes in autoload from 2.0 to 2.1. The above code works fine with the latest version, to which I upgraded my project ;-)
For a simple case like this the quickest solution is creating a folder (for example Common) directly under src and put your class in it.
src
-- Common
-- Tools.php
Tools.php contains your class with proper namespace, for example
<?php
namespace Common;
class Tools
{
public static function slugify($string)
{
// ...
}
}
Before calling your function do not forget the use statement
use Common\Tools;
// ...
Tools::slugify('my test string');
If you put your code under src following the proper folder structure and namespace as above, it will work without touching app/autoload.php.

Resources