How to configure `I18n Ally` vscode plugin to read my locals? - i18next

I am using i18next and react-i18next.
i18n Ally v2.8.1.
I have one locale file: /locales/en.json
Structure of this file:
{
"pagetitle.home": "Home",
"pagetitle.restore": "Restore",
"pagetitle.register": "Register"
}
When hover on code i18n.t('pagetitle.restore')
ru: i18n key "en.pagetitle.restore" does not exist(i18n-ally-key-missing)
Which config of extension should be?
P.S. I cant change locales structure.

Try changing the .vscode/settings.json to add your "locals" path:
{
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.sourceLanguage": "english",
}
If this does not work, try to add a defaultNamespace to your language file:
Example:
en.json
{
"translation": {
"login": {
"title": "Welcome!",
"user": "User",
"password": "Password",
},
}
}
Implementation:
const {t} = useTranslation();
const title = title: t('login.title')
.vscode/settings.json
{
"i18n-ally.localesPaths": ["src/utils/language"],
"i18n-ally.defaultNamespace": "translation",
"i18n-ally.sourceLanguage": "english",
"i18n-ally.keystyle": "nested"
}

Related

Commitlint - Allow '/' in scope-enum

In my Angular project, I want to extend #commitlint/config-conventional with some pre-defined scopes.
The Angular project has a library for UI components (generated via ng generate library) and a default app which consumes the UI library.
In commitlint.config.js I've added the following lines:
module.exports = {
extends: ['#commitlint/config-conventional'],
rules: {
'scope-enum': [
2,
'always',
[
'ui-components',
'ui-components/badge',
'ui-components/button',
'ui-components/tooltip',
'core',
'account',
'plugins',
'settings',
'projects',
'shared',
'styles'
]
]
}
};
However, when I try to commit something with the scope: 'ui-components/tooltip':
fix(ui-components/tooltip): fix border
I get a commitlint error, saying that:
⧗ input: fix(ui-components/tooltip): fix border
✖ scope must be one of [ui-components, ui-components/badge, ui/button, ui-components/tooltip, core, account, plugins, settings, projects, shared, styles] [scope-enum]
✖ found 1 problems, 0 warnings
Unfortunately slashes aren't allowed in scopes.
To get around this I replace / with two dashes (--).
I wrote a script to grab subfolders and return an array:
https://gist.github.com/trevor-coleman/51f1730044e14081faaff098618aba36
[
'ui-components',
'ui-components--badge',
'ui-components--button',
'ui-components--tooltip',
...
]
According to source code, Commitlint use / for multiple scopes.
It means, you can commit like fix(core/account): fix border but you can't commit fix(ui-components/tooltip): fix border because you need to add tooltip in to your scopes first.
Here is source code: https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/rules/src/scope-enum.ts
Also, it is mentioned in here: https://github.com/conventional-changelog/commitlint/blob/master/docs/concepts-commit-conventions.md#multiple-scopes
You can write your own custom plugin to check scopes, I had the same issue, so I wrote one to solve this problem, see example commitlint.config.js below:
module.exports = {
extends: ["#commitlint/config-conventional"],
rules: {
"enhanced-scope-enum": [
2,
"always",
[
"ui-components",
"ui-components/badge",
"ui-components/button",
"ui-components/tooltip",
"core",
"account",
"plugins",
"settings",
"projects",
"shared",
"styles",
],
],
},
plugins: [
{
rules: {
"enhanced-scope-enum": (parsed, when = "always", value = []) => {
if (!parsed.scope) {
return [true, ""];
}
// only use comma sign as seperator
const scopeSegments = parsed.scope.split(",");
const check = (value, enums) => {
if (value === undefined) {
return false;
}
if (!Array.isArray(enums)) {
return false;
}
return enums.indexOf(value) > -1;
};
const negated = when === "never";
const result =
value.length === 0 ||
scopeSegments.every((scope) => check(scope, value));
return [
negated ? !result : result,
`scope must ${negated ? `not` : null} be one of [${value.join(
", "
)}]`,
];
},
},
},
],
}

Adding custom rules in Solhint

I am using solhint plugin for linting solidity code. But I want to add custom rules for the code analysis. How to add custom rules as part of the ruleset ?
Code added for custom rule:
const BaseChecker = require('./../base-checker')
const ruleId = 'no-foos'
const meta = {
type: 'naming',
docs: {
description: `Don't use Foo for Contract name`,
category: 'Style Guide Rules'
},
isDefault: false,
recommended: true,
defaultSetup: 'warn',
schema: null
}
class NoFoosAllowed extends BaseChecker {
constructor(reporter) {
super(reporter, ruleId, meta)
}
ContractDefinition(ctx) {
const { name } = ctx
if (name === 'Foo') {
this.reporter.error(ctx, this.ruleId, 'Contracts cannot be named "Foo"')
}
}
}
module.exports = NoFoosAllowed
I have saved the above code into a new js file inside rules->naming folder. And i have used the 'no-foos' rule id inside my .solhint.json file inside the rules property.
{
"extends": "solhint:all",
"plugins": [],
"rules": {
"avoid-suicide": "error",
"avoid-sha3": "warn",
"no-foos" : "warn",
"var-name-mixedcase": "error"
}
}
Each ruleset loops through all rules and enables (or doesn't enable) it based on the rule metadata and the ruleset config.
So you can create a custom rule in the rules folder and set it a combination of metadata that your ruleset will enable.

How do I annotate an endpoint in NestJS for OpenAPI that takes Multipart Form Data

My NestJS server has an endpoint that accepts files and also additional form data
For example I pass a file and a user_id of the file creator in the form.
NestJS Swagger needs to be told explicitly that body contains the file and that the endpoint consumes multipart/form-data this is not documented in the NestJS docs https://docs.nestjs.com/openapi/types-and-parameters#types-and-parameters.
Luckily some bugs led to discussion about how to handle this use case
looking at these two discussions
https://github.com/nestjs/swagger/issues/167
https://github.com/nestjs/swagger/issues/417
I was able to put together the following
I have added annotation using a DTO:
the two critical parts are:
in the DTO add
#ApiProperty({
type: 'file',
properties: {
file: {
type: 'string',
format: 'binary',
},
},
})
public readonly file: any;
#IsString()
public readonly user_id: string;
in the controller add
#ApiConsumes('multipart/form-data')
this gets me a working endpoint
and this OpenAPI Json
{
"/users/files":{
"post":{
"operationId":"UsersController_addPrivateFile",
"summary":"...",
"parameters":[
],
"requestBody":{
"required":true,
"content":{
"multipart/form-data":{
"schema":{
"$ref":"#/components/schemas/UploadFileDto"
}
}
}
}
}
}
}
...
{
"UploadFileDto":{
"type":"object",
"properties":{
"file":{
"type":"file",
"properties":{
"file":{
"type":"string",
"format":"binary"
}
},
"description":"...",
"example":"'file': <any-kind-of-binary-file>"
},
"user_id":{
"type":"string",
"description":"...",
"example":"cus_IPqRS333voIGbS"
}
},
"required":[
"file",
"user_id"
]
}
}
Here is what I find a cleaner Approach:
#Injectable()
class FileToBodyInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const ctx = context.switchToHttp();
const req = ctx.getRequest();
if(req.body && req.file?.fieldname) {
const { fieldname } = req.file;
if(!req.body[fieldname]) {
req.body[fieldname] = req.file;
}
}
return next
.handle();
}
}
const ApiFile = (options?: ApiPropertyOptions): PropertyDecorator => (
target: Object, propertyKey: string | symbol
) => {
ApiProperty({
type: 'file',
properties: {
[propertyKey]: {
type: 'string',
format: 'binary',
},
},
})(target, propertyKey);
};
class UserImageDTO {
#ApiFile()
file: Express.Multer.File; // you can name it something else like image or photo
#ApiProperty()
user_id: string;
}
#Controller('users')
export class UsersController {
#ApiBody({ type: UserImageDTO })
// #ApiResponse( { type: ... } ) // some dto to annotate the response
#Post('files')
#ApiConsumes('multipart/form-data')
#UseInterceptors(
FileInterceptor('file'), //this should match the file property name
FileToBodyInterceptor, // this is to inject the file into the body object
)
async addFile(#Body() userImage: UserImageDTO): Promise<void> { // if you return something to the client put it here
console.log({modelImage}); // all the fields and the file
console.log(userImage.file); // the file is here
// ... your logic
}
}
FileToBodyInterceptor and ApiFile are general, I wish they where in the NestJs
You probably need to install #types/multer to have to Express.Multer.File

How to define messages with react-i18next

I'd like to ask a question about whether react-i18next provides us a way to define messages as react-intl
I wanna define all my messages first then using i18next-scanner to extract to json file.
Thanks in advance for your help in this matter.
After a while of researching. I'd give my way to someone wanna define messages in such a messages.js. BTW, I'm using i18nxt-scanner to extract the messages to JSON file.
i18next-scanner.config.js
module.exports = {
input: [
'src/app/**/*.{js,jsx}',
// Use ! to filter out files or directories
'!app/**/*.spec.{js,jsx}',
'!app/i18n/**',
'!**/node_modules/**',
],
output: './',
options: {
debug: true,
removeUnusedKeys: true,
func: {
list: ['getTranslationId'],
extensions: ['.js', '.tsx'],
},
lngs: ['en', 'ko'],
ns: ['translation'],
defaultLng: 'en',
defaultNs: 'translation',
defaultValue: '',
resource: {
loadPath: 'src/locales/{{lng}}/{{ns}}.json',
savePath: 'src/locales/{{lng}}/{{ns}}.json',
jsonIndent: 2,
lineEnding: '\n',
},
nsSeparator: false, // namespace separator
keySeparator: false, // key separator
interpolation: {
prefix: '{{',
suffix: '}}',
},
},
};
messages.js
import { getTranslationId } from 'locales/utils';
const messages = {
title: getTranslationId('Homepage_title', {
defaultValue: 'hello, {{name}}',
}),
count: getTranslationId('Homepage_count', {
defaultValue: '{{count}} time',
count: 0, // for plurals
}),
};
export default messages;
locales/utils.js
export const getTranslationId = id => {
if (!id || !id.includes('_'))
throw new Error('ID pattern should be "BLOCK_ElEMENT"');
return id;
};
Extracting command
yarn run i18next-scanner
Hope this helps! :D

i18next - All languages in one .json file

How can I make i18next load all languages from just one file?
I managed to do it by putting each language in a seperate file (translation-en.json, translation-no.json, etc), and also managed to input languages with the resStore option, but putting it all in a seperate .json file is really not documented anywhere (I've searched for 4 hours+ now)
My js code:
i18n.init({
debug: true,
lng: 'en',
resGetPath: 'translation.json'
},
function(t) {
console.log(t('test'));
});
My translation.json file:
{
en: {
translation: {
test: "some string"
}
},
no: {
translation: {
test: "litt tekst"
}
}
}
Ok, so I managed to "hack" it byt putting an object into a seperate .js file, include it in a script tag and loading it using resStore, but that just can't be the best way to use this lib.
Assume that your translation.json has loaded and assigned to a variable named resStore:
var resStore = {
en: {
translation: {
test: "some string"
}
},
no: {
translation: {
test: "litt tekst"
}
}
};
Next, you can override default ajax loading functionality with your customLoad function. An example might look like this:
var options = {
lng: 'en',
load: 'current',
lowerCaseLng: true,
fallbackLng: false,
resGetPath: 'i18n/__lng__/__ns__.json',
customLoad: function(lng, ns, options, loadComplete) {
var data = resStore[lng][ns];
loadComplete(null, data); // or loadComplete('some error'); if failed
},
ns: {
namespaces: ['translation'],
defaultNs: 'translation'
}
};
i18n.init(options, function(t) {
t('test'); // will get "some string"
});
new update on Mar 20, 2015
You can simply pass your resource store with the resStore option:
i18n.init({ resStore: resources });