Giter Club home page Giter Club logo

crowdin-api-client-js's Introduction

Crowdin JavaScript client Tweet GitHub Repo stars

The Crowdin JavaScript client is a lightweight interface to the Crowdin API that works in any JavaScript environment, including web browsers, workers in web browsers, extensions in web browsers or desktop applications, Node.js etc. It provides common services for making API requests.

Our API is a full-featured RESTful API that helps you to integrate localization into your development process. The endpoints that we use allow you to easily make calls to retrieve information and to execute actions needed.

Table of Contents

Installation

npm

npm i @crowdin/crowdin-api-client

yarn

yarn add @crowdin/crowdin-api-client

Quick Start

Typescript
import crowdin, { Credentials, SourceFilesModel } from '@crowdin/crowdin-api-client';

// credentials
const credentials: Credentials = {
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
};

// initialization of crowdin client
const {
  projectsGroupsApi,
  uploadStorageApi,
  sourceFilesApi,
  translationsApi
} = new crowdin(credentials);

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

// You can also use async/wait. Add `async` keyword to your outer function/method
async function getProjects() {
  try {
    const projects = await projectsGroupsApi.listProjects();
    console.log(projects);
  } catch (error) {
    console.error(error);
  }
}

// Create file with json content to translate
async function createFile() {
  const projectId = 123;
  const fileData = {
    title: 'Example',
    description: 'Some Text'
  };
  const storage = await uploadStorageApi.addStorage('file1.json', fileData);
  const file = await sourceFilesApi.createFile(projectId, {
    name: 'file1.json',
    title: 'Sample file',
    storageId: storage.data.id,
    type: 'json'
  });
  console.log(file);
}

// Download translations
async function downloadTranslations() {
  const projectId = 123;
  const fileId = 456;
  const language = 'de';
  const downloadLink = await translationsApi.buildProjectFileTranslation(
    projectId,
    fileId,
    {
      targetLanguageId: language
    }
  );
  const response = await fetch(downloadLink.data.url);
  const translations = await response.json();
  console.log(translations);
}

Or specific API instances:

import { Credentials, ProjectsGroups } from '@crowdin/crowdin-api-client';

// credentials
const credentials: Credentials = {
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
};

// initialization of ProjectsGroups
const projectsGroupsApi = new ProjectsGroups(credentials);

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));
Javascript ES6 modules
import crowdin from '@crowdin/crowdin-api-client';

// initialization of crowdin client
const {
  projectsGroupsApi,
  uploadStorageApi,
  sourceFilesApi,
  translationsApi
} = new crowdin.default({
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
});

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

// You can also use async/wait. Add `async` keyword to your outer function/method
async function getProjects() {
  try {
    const projects = await projectsGroupsApi.listProjects();
    console.log(projects);
  } catch (error) {
    console.error(error);
  }
}

// Create file with json content to translate
async function createFile() {
  const projectId = 123;
  const fileData = {
    title: 'Example',
    description: 'Some Text'
  };
  const storage = await uploadStorageApi.addStorage('file1.json', fileData);
  const file = await sourceFilesApi.createFile(projectId, {
    name: 'file1.json',
    title: 'Sample file',
    storageId: storage.data.id,
    type: 'json'
  });
  console.log(file);
}

// Download translations
async function downloadTranslations() {
  const projectId = 123;
  const fileId = 456;
  const language = 'de';
  const downloadLink = await translationsApi.buildProjectFileTranslation(
    projectId,
    fileId,
    {
      targetLanguageId: language
    }
  );
  const response = await fetch(downloadLink.data.url);
  const translations = await response.json();
  console.log(translations);
}

Or specific API instances:

import { ProjectsGroups } from '@crowdin/crowdin-api-client';

// initialization of ProjectsGroups
const projectsGroupsApi = new ProjectsGroups({
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
});

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));
Javascript CommonJS
const crowdin = require('@crowdin/crowdin-api-client');

// initialization of crowdin client
const {
  projectsGroupsApi,
  uploadStorageApi,
  sourceFilesApi,
  translationsApi
} = new crowdin.default({
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
});

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

// You can also use async/wait. Add `async` keyword to your outer function/method
async function getProjects() {
  try {
    const projects = await projectsGroupsApi.listProjects();
    console.log(projects);
  } catch (error) {
    console.error(error);
  }
}

// Create file with json content to translate
async function createFile() {
  const projectId = 123;
  const fileData = {
    title: 'Example',
    description: 'Some Text'
  };
  const storage = await uploadStorageApi.addStorage('file1.json', fileData);
  const file = await sourceFilesApi.createFile(projectId, {
    name: 'file1.json',
    title: 'Sample file',
    storageId: storage.data.id,
    type: 'json'
  });
  console.log(file);
}

// Download translations
async function downloadTranslations() {
  const projectId = 123;
  const fileId = 456;
  const language = 'de';
  const downloadLink = await translationsApi.buildProjectFileTranslation(
    projectId,
    fileId,
    {
      targetLanguageId: language
    }
  );
  const response = await fetch(downloadLink.data.url);
  const translations = await response.json();
  console.log(translations);
}

Or specific API instances:

const ProjectsGroups = require('@crowdin/crowdin-api-client').ProjectsGroups;

// initialization of ProjectsGroups
const projectsGroupsApi = new ProjectsGroups({
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
});

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

You can generate Personal Access Token in your Crowdin Account Settings.

For more examples please check Examples

List of projects with Fetch API

In addition if you use client in non-Node.js environment you might have a troubles with http calls. This client uses axios which internally uses http and https Node modules. So there is an option to use http client based on Fetch API (keep in mind that fetch should be available in global scope).

import { ProjectsGroups } from '@crowdin/crowdin-api-client';

const projectsGroupsApi = new ProjectsGroups(credentials, {
  httpClientType: 'fetch'
});

Or even pass your own http client as httpClient property which should implement HttpClient interface.

Fetch all records

It is possible to fetch all records from paginatable methods (where we have limit and offset in arguments).

import { ProjectsGroups } from '@crowdin/crowdin-api-client';

// initialization of ProjectsGroups
const projectsGroupsApi = new ProjectsGroups({
  token: 'personalAccessToken',
  organization: 'organizationName' // optional
});

// get all projects
projectsGroupsApi
  .withFetchAll()
  .listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

// get projects but not more than 1000
projectsGroupsApi
  .withFetchAll(1000)
  .listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

Retry configuration

There is a possibility to configure client invoke http calls with retry mechanism.

import { ProjectsGroups } from '@crowdin/crowdin-api-client';

const projectsGroupsApi = new ProjectsGroups(credentials, {
  retryConfig: {
    retries: 2, // amount of retries (gte 0)
    waitInterval: 100, // wait interval in ms between retries
    conditions: [ // array of conditions which will check if retry should not be applied
      {
        test(error) {
          return error.code === 40
        }
      }
    ]
  }
});

Exception handling

In case of error library will throw an Error based exception. This can either be a generic error with an error message and a code, or a validation error that additionally contains validation error codes.

const crowdin = require('@crowdin/crowdin-api-client');

const token = '';

const { translationsApi } = new crowdin.default({ token });

async function test() {
  const project = 123;
  const dir = 456;
  try {
    const res = await translationsApi.buildProjectDirectoryTranslation(project, dir);
    console.log(JSON.stringify(res));
  } catch (e) {
    if (e instanceof crowdin.CrowdinValidationError) {
      console.log('Validation error');
    } else if (e instanceof crowdin.CrowdinError) {
      console.log('Generic error');
    }
    console.error(e);
  }
}

test();

Http request timeout

By default request timeout will vary on http client implementation and/or environment (e.g. fetch uses timeout configured by the browser).
But there is an option to set constant value:

const crowdin = require('@crowdin/crowdin-api-client');

const credentials = { token: 'token' };

const httpRequestTimeout = 60 * 1000; // 60 seconds

const client = new crowdin.default(credentials, { httpRequestTimeout });

Over-The-Air Content Delivery

💫 Recommended for translations delivery to your website or mobile application.

You can also use the Crowdin OTA Client JS library to send the translated content to your web apps via content delivery. Crowdin Content Delivery uses a CDN vault that mirrors your project’s translated content. The updated translations will become available to users much faster.

GraphQL API

This library also provides possibility to use GraphQL API (only for Crowdin Enterprise).

const crowdin = require('@crowdin/crowdin-api-client');

const client = new crowdin.default({
  token: '{token}',
  organization: '{organization}'
});

const query = `
query {
  viewer {
    projects(first: 50) {
      edges {
        node {
          name
  
          files(first: 10) {
            totalCount
            edges {
              node {
                name
                type
              }
            }
          }
        }
      }
    }
  }
}
`;

client
  .graphql({ query })
  .then(res => console.log(JSON.stringify(res, null, 2)));

Seeking Assistance

If you find any problems or would like to suggest a feature, please read the How can I contribute section in our contributing guidelines.

Need help working with Crowdin JavaScript client or have any questions? Contact Customer Success Service.

Contributing

If you want to contribute please read the Contributing guidelines.

Security

FOSSA Status

License

The Crowdin JavaScript client is licensed under the MIT License.
See the LICENSE.md file distributed with this work for additional
information regarding copyright ownership.

Except as contained in the LICENSE file, the name(s) of the above copyright
holders shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written authorization.

crowdin-api-client-js's People

Contributors

ade1705 avatar aksbad007 avatar alexandrtovmach avatar andrii-bodnar avatar balogun14 avatar cdaringe avatar dependabot[bot] avatar dies avatar encrypteddev avatar faizan1191 avatar imrodry avatar irinayeee avatar jackassmc avatar jamesmhenderson avatar jenistenxavier avatar jwunderl avatar kevinb-tryjeeves avatar kisharnath avatar kylegrande avatar minimalsm avatar nazarlysyi avatar orkisz avatar romandolinovskyi avatar souravghosh01 avatar vainock avatar vbeytok avatar viliam-jobko avatar yevheniyj avatar yhorodyskyi avatar zahid92 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

crowdin-api-client-js's Issues

v1 API Keys are broken

After the latest release the API Keys for API v1 stooped to work.

Steps to reproduce:

  1. Go to the https://crowdin.com/settings#api-key
  2. Copy or Generate new API key under the "Account API key" title
  3. Take an appropriate Project identifier for API V1 in the "API & Webhooks" menu tab.
  4. Make a request to https://api.crowdin.com/api/project/{project-identifier}/download/all.zip?key={project-key}

The response after using the updated API key:
<?xml version="1.0" encoding="UTF-8"?> <error> <code>1</code> <message>Requested project doesn't exist or the API key is invalid</message> </error>

While the old API keys that were generated before the update still work.

Typescript types do not line up with API documentation for Tasks#addTask

Steps to reproduce:

Given the following code, run it through the typescript compiler:

import {
  Tasks,
} from '@crowdin/crowdin-api-client';


return new Tasks({ token: 'foo-bar-baz-blah' }).addTask(1111111, {
  title: 'translate',
  fileIds: [3],
  languageId: 'es',
  type: 0,
  assignees: [{ id: 1 }],
});

Expected behavior:

This should compile as the specified arguments handed to addTask are what are marked as required in the docs at https://support.crowdin.com/api/v2/#operation/api.projects.tasks.post

Actual behavior:

I get the following typescript error due to me only supplying the id property of the assignee

TSError: ⨯ Unable to compile TypeScript:
error TS2739: Type '{ id: number; }' is missing the following properties from type 'Assignee': username, fullName, avatarUrl, wordsCount, wordsLeft

       assignees: [{ id: 1 }],
                   ~~~~~~~~~~~~~~~~~~~~

From reading the docs, it seems like I should only have to pass id and nothing else. But the exported interface makes all of the properties required:

https://github.com/crowdin/crowdin-api-client-js/blob/master/src/tasks/index.ts#L217

I'd assume the correct fix for this is to just mark all but id as optional by tacking on a ? at the end of them.

GenerateReport API issues

  1. The enum entry of ReportsModel.Format is missing JSON, which should exist according to the API Reference.
  2. The field languageId of TopMembersSchema seems to be required, despite it not actually being required. You can send a request (e. g. via Postman) without this field and generate a report with all languages.
  3. How do I set dateFrom and dateTo for TopMembersSchema? Am I missing something or is it a bug? If I try to set those fields besides my unit, format and languageId, VSC tells me it is not assignable. If I look into src/reports/index.js, they also seem to be missing.

Please correct me if these things are not actually issues, but intended behavior.

RFC: Reject and throw only error instances

Problem

Crowdin throws/rejects non-errors, which is generally considered an anti-pattern.

if (error.response.status === 400) {
  return Promise.reject(error.response.data);
}                

Discussion

Error instances have predictable control flow semantics. Though it can never be safely assumed,

try {
  foo()
} catch (err) {
  err // where`err instanceof Error` yields `true`
}

is generally considered a best practice. In this regard, error handlers can pick off message & stack accordingly.

Recommendation

Throw the original Error, or, create a new Error instance that extends Error to decorate your values on.

Missing Authorization header when using FETCH

I'm unable to get the client working with FETCH. My code:

import CROWDIN, { HttpClientType } from "@crowdin/crowdin-api-client";

async function a() {
  return (
    await new CROWDIN(
      { token: API_TOKEN},
      { httpClientType: HttpClientType.FETCH }
    ).projectsGroupsApi.getProject(PROJECT_ID)
  ).data;
}

console.log(a())

When executing this code in the browser it successfully sends a request to https://api.crowdin.com/api/v2/projects/PROJECT_ID but returns a 401, that's because it's missing the Authorization header.
(The API key and project id I'm providing are correct, but that seems not to be the issue.)

Make axios optional dependency

As client can work with different http client implementations the default implementation and the library which is used in there might not be needed.

So the axios package can be moved to the optionalDependencies section.
But it will cause a problems in existing projects as this client is used almost everywhere with the default configuration (default axios-based http client).

With that being said this enhancement should be shipped in a new major release and should be followed with proper informative error handling (check if axios is installed and if not the proper error message should be displayed) and updated documentation.

Leading whitespace is getting trimmed when adding a translation to a string in a chrome JSON source file

Hello all, I've noticed what looks like a bug in either this library or the handling of API v2 requests by the crowdin platform.

If I have a project with a chrome JSON source file and programmatically add a translation that contains leading whitespace characters then the whitespace characters are trimmed from the submitted translation at some point. This can be seen both when the translation is viewed in the crowdin UI and when returned by the API.

I don't think this is correct and the crowdin UI itself agrees: highlighting that if the source string has leading whitespace, then that should be preserved in translations.

Screenshot 2020-12-03 at 17 10 06

This gist contains sample code that replicates the bug. It creates a new source file (example.chrome.json) populated with a string with leading whitespace. It then translates that string into Danish, preserving the leading whitespace.

Many thanks, Chris

Type issue inside addStorage

hey 👋

I spent quite a few minutes trying to understand why my upload failed.
I was sending a JSON file and got a ... type: chrome from the API instead of the custom_fomat I was expecting (what I have if I upload via the UI).

Based on the source I had no idea what the lib wanted to have

So I looked into the tests and ok, you use a string https://github.com/crowdin/crowdin-api-client-js/blob/master/tests/uploadStorage/api.test.ts#L21

nope 😁

We must send a buffer in order to have the API doing its magic to get the correct type value (as sending a type=custom_format === 500 from the API)

Based on the API doc:

string (Binary file data) File content. Text of file or any other raw binary data.

I would expect a buffer


Can you add a custom type and describe what we can use, and what to expect?

Thx 🙏

Add fetchAll helper

Nice to have a function to fetch all items recursively to increase default pagination limits

Question: Get actual text of source string

Hello, I'm very new to JavaScript programming, so this question may seem silly, but I have no clue what I should Google.

I've got a String object from the List Strings API method and I want to get the text of this source string as a (JavaScript) string, but when I try to
var text:string = sourceString.data.text
VSCode outputs:

Type 'string | PluralText' is not assignable to type 'string'.
Type 'PluralText' is not assignable to type 'string'.

I know that the problem lies here, but I don't know what I can do about this. How can I get the actual text of this string? Btw, the Crowdin project does not contain any source strings that have the plural form.

RFC: Refactor handleError to explicitly accept Error instances

Problem

Our SDK uses crowdin, however, crowdin emits unhelpful errors.

Specifically, generic errors are produced here:

const message = error.error?.message || 'Error occured';

Discussion

  • handleError should be refactored to expect only error instance.
  • all callsites that are not passing explict Error instances will begin to fail tsc
  • every callsites in violation of not passing an Error instance should coerce their failure into an Error instance, adding the context that is currently missing in handleError.

because handleError accept any, generic errors happen and the user experience is poor. developers do not have any inclination as to the failure mode. this is easily solved by forcing all handleError callsites to provide Error instances with adequate context in their message fields

CreateTaskRequest has incorrectly required field: workflowStepId

CreateTaskRequest has incorrectly required field: workflowStepId.

Spotted when using the client (version 1.8.12) to add tasks from TypeScript.

It's defined as:

    export interface CreateTaskRequest {
        workflowStepId: number;
        // ...
     }

See

workflowStepId: number;

I can successfully create tasks without this field. As far as I can tell, the OpenAPI definition (from https://support.crowdin.com/api/v2/) doesn't define this field for the add/create operation at all, only for Task itself.

Type ProjectsGroupsModel.Project missing targetLanguages property

Example

projectsGroupsApi.getProject(417404).then(res=>{
    console.log(res.data.targetLanguages) //Property 'targetLanguages' does not exist on type 'Project | ProjectSettings'
})

Log

Array [
    Object {
      "androidCode": "es-rES",
      "dialectOf": null,
      "editorCode": "es",
      "id": "es-ES",
      "locale": "es-ES",
      "name": "Spanish",
      "osxCode": "es.lproj",
      "osxLocale": "es",
      "pluralCategoryNames": Array [
        "one",
        "other",
      ],
      "pluralExamples": Array [
        "1",
        "0, 2-999; 1.2, 2.07...",
      ],
      "pluralRules": "(n != 1)",
      "textDirection": "ltr",
      "threeLettersCode": "spa",
      "twoLettersCode": "es",
    },
    Object {
      "androidCode": "en-rUS",
      "dialectOf": null,
      "editorCode": "en",
      "id": "en",
      "locale": "en-US",
      "name": "English",
      "osxCode": "en.lproj",
      "osxLocale": "en",
      "pluralCategoryNames": Array [
        "one",
        "other",
      ],
      "pluralExamples": Array [
        "1",
        "0, 2-999; 1.2, 2.07...",
      ],
      "pluralRules": "(n != 1)",
      "textDirection": "ltr",
      "threeLettersCode": "eng",
      "twoLettersCode": "en",
    },
  ]

Use different Axios instance

Hello, I have trouble changing the http client to a different Axios instance.

import Axios from 'axios';
const axios = Axios.create(); // my instance works correctly and I'm able to send requests without any trouble
const { projectsGroupsApi } = new Crowdin({ token: 'myToken' }, { httpClient: axios}); // I already tried setting httpClientType to HttpClientType.AXIOS but this doesn't do anything

This does not work and the client is rather calling its own internal instance.
If you could provide a sample of how to achieve this I would be very grateful!

Lsc

chrome-distiller://58dec71d-4961-4b19-9f3b-a6683441aa84_44fb6663b51c153ef8e0549fb5c421c4964abdcdd8ba038b97172c065043a656/?title=Creative+Commons+Legal+Code&url=https%3A%2F%2Fcreativecommons.org%2Flicenses%2Fby%2F2.5%2Flegalcode

File ImportOptions improvement

We've added new possible import options for files:

  • Add File: new ImportOptions possible value - Docx File Import Options (including the response)

  • Update or Restore File: new ImportOptions possible value - Docx File Import Options (including the response)

  • Edit File: new /importOptions/* path parameters

So these changes should be reflected in this API Client.

Allow limit and offset to be passed to uploadStorage.listStorages

It appears currently not possible to pass pagination options to the storage API listStorages method.

 listStorages(): Promise<ResponseList<UploadStorageModel.Storage>>

It shows as supported by the rest API (https://support.crowdin.com/api/v2/#operation/api.storages.getMany) so I assume it should be easy enough to implement in the same way as other list methods? Would be really handy for me if this could be added (and thanks for the awesome lib).

Library overhaul proposal

Hey! I'm making this issue as a way to propose a big change to the library as a whole. The issue will be split into different sections explaining my motivations for this, as well as possible solutions I've considered

So, what's wrong?

Unsafe types

Currently, the library mixes regular Crowdin API endpoints with Crowdin Enterprise API endpoints and, while some are shared, Enterprise, unfortunately, has a lot of endpoints that regular Crowdin does not and in some common endpoints, the responses are different, which has led to issues in types like the one solved in #132. These factors lead to a very type-unsafe library, which is not good considering the library is even built in TypeScript natively.

Simplicity of the library

Another issue I've faced while using the package is that it is very simple, in the sense that it doesn't offer any helper functions or data transformation, and it simply gives you 1 method for each endpoint and returns the exact data that the API returns. In order to better explain this point, I'll divide it into two sections

Transforming API data

While it's nice that you give us the raw data received from the API call, I don't think this is enough. This could be improved by creating classes that house methods to manage specific structures instead of having to call the endpoint and pass the arguments ourselves. A very good example of this is how the discord.js package wraps around the Discord API providing managers, classes for every Discord structure, and methods to edit and manage them directly, without the developer ever having to access the API directly. Without this, the library simply feels like a slightly customized version of axios, and there's no real advantage to using it instead of simply crafting our requests ourselves using the aforementioned package.

Helper methods

As mentioned previously, the library lacks helper methods for certain parts of the API. For example, the API has a hard limit of 500 structures that can be returned per call, but sometimes we need to obtain more than 500 structures, so we need multiple API calls to achieve this. This results in us developers having to make functions like this.

async function getStrings(stringsApi: SourceStrings, project: ProjectsGroupsModel.ProjectSettings) {
	let moreThan500 = true,
		strings: SourceStringsModel.String[] = [];

	while (moreThan500) {
		const projectStrings = (
			await stringsApi.listProjectStrings(project.id, {
				limit: 500,
				offset: strings.length,
				croql: "not is hidden"
			})
		).data;
		strings = strings.concat(projectStrings.map(m => m.data));

		moreThan500 = projectStrings.length === 500;
	}

	return strings;
}

In this function, I am simply getting all the strings in a project, but I have to create this while loop in order to achieve that, something that could easily be done on the library's side.

Possible solution

As described earlier, an example of an API wrapper that I personally really like is discord.js which achieves all of the points mentioned above, however, Crowdin is different in some ways so allow me to summarize the ideas I had for the library overall:

  1. Create 2 client initializers: one for regular Crowdin and another one for CrowdinEnterprise (could use these names for the names of the classes even)
  2. Store all common endpoints (same call structure and response) in a base client class: BaseCrowdinClient (private)
  3. Upon initializing either one of the clients, the library could fetch all projects the account has access to (using the projects.getMany endpoint) and cache them in a projects manager
  4. All methods should be housed within said manager, and each one of the APIs should be a manager of its own either on the client or the project, whether it depends on it or not.
  5. All data should be transformed to remove the data property and provide a much more developer-friendly object, while still allowing access to the raw data through a property on the class.

I would love to contribute to a possible rewrite of the library including the features mentioned here, so I'd like to hear your feedback, both from the maintainers and the community that uses this package so that we can reach an agreement and improve the library. Thank you for reading through!

Automatic update to Dependencies using Dependabot.

Using outdated packages raises security concerns and updating the packages is an essential step. But this step can be automated.
Updates to packages can no longer need to be a manual task. Dependabot can be used to identify the dependencies that have update and it can raise a PR.

Examples missing

Hi there,

is there any documentation how the client works with the API? Besides the examples in readme there is nothing to understand how to make all of the different calls...

Thank you!

Glossaries API updates

We're happy to announce a significant update for Crowdin Glossaries that allows managing the terminology in a more advanced way.

There are a lot of new things and some changes in the Crowdin API.

New APIs:

Updates:

The translationOfTermId is deprecated and the conceptId added in:

Edit Term - new path options

All of these changes should be reflected in this API Client and covered by Unit tests.

crowdin is not a constructor

i want to use it through node.js,but i got this error:crowdin is not a constructor
the code is the example code,it's this can't work in node.js enviroment?please tell me how to fix it,thanks
for safe i hide the token

const crowdin = require('@crowdin/crowdin-api-client')

console.log(crowdin.toString());

// initialization of crowdin client
const { projectsGroupsApi } = new crowdin({
  token: 'mytoken',
});

// get project list
projectsGroupsApi.listProjects()
  .then(projects => console.log(projects))
  .catch(error => console.error(error));

// You can also use async/wait. Add `async` keyword to your outer function/method
async function getProjects() {
  try {
    const projects = await projectsGroupsApi.listProjects();
    console.log(projects);
  } catch (error) {
    console.error(error);
  }
}

Project's files list with branchId filter causes an empty result

Hello. Calling crowdin.sourceFilesApi.listProjectFiles() function with the second argument (branchId) causes an empty result (branch exists). Am I doing something wrong or it's expected behavior?

Curl request:

curl -X GET 'https://api.crowdin.com/api/v2/projects/350687/files?branchId=26' \
    -H 'Authorization: Bearer *masked*'

Response:

{
    "data": [],
    "pagination": {
        "offset": 0,
        "limit": 25
    }
}

Without the branchId param, the response contains the necessary file and you can see, that branchId is correct.

{
    "data": [
        {
            "data": {
                "revisionId": 5,
                "status": "active",
                "priority": "high",
                "importOptions": null,
                "exportOptions": null,
                "createdAt": "2019-03-04T12:26:21+00:00",
                "updatedAt": "2020-05-23T23:20:48+00:00",
                "revision": 5,
                "id": 6,
                "projectId": 350687,
                "branchId": null,
                "directoryId": 4,
                "name": "site.json",
                "title": null,
                "type": "json"
            }
        },
        {
            "data": {
                "revisionId": 1,
                "status": "active",
                "priority": "normal",
                "importOptions": null,
                "exportOptions": null,
                "createdAt": "2020-06-08T23:25:30+00:00",
                "updatedAt": "2020-06-08T23:25:31+00:00",
                "revision": 1,
                "id": 32,
                "projectId": 350687,
                "branchId": 26,
                "directoryId": 30,
                "name": "site.json",
                "title": null,
                "type": "json"
            }
        }
    ],
    "pagination": {
        "offset": 0,
        "limit": 25
    }
}

Encode the Crowdin-API-FileName

According to the documentation, the Crowdin-API-FileName header value should be url encoded.

config.headers['Crowdin-API-FileName'] = fileName;

Currently, this value is not encoded and that is causing issues with uploading files, for example, with Cyrillic file names.

Also, a new test case should be added

describe('Upload Storage API', () => {

buildProjectDirectoryTranslation method fails with error "Unsupported Content-Type Header"

While some methods work properly (e.g. buildProject) this one fails with exception:

{
  "message": "Unsupported Content-Type Header",
  "code": 415
}

Performing my request like this
let {data: {id}} = await translationsApi.buildProjectDirectoryTranslation(PROJECT_ID, DIRECTORY_ID);

Also then I tried to make request with axios, it was successful as well:

let data = await axios.post(`https://libertexgroup.crowdin.com/api/v2/projects/${PROJECT_ID}/translations/builds/directories/${DIRECTORY_ID}`, {}, {
    headers: {
        Authorization: `Bearer ${API_KEY}`
    }
});

Environment:
nodejs v16.13.1
crowdin-api-client-js v1.17.0

Proposal: switch from axios to undici

Node.js has recently implemented the fetch API into it as an experimental flag (ref: nodejs/node#41749) which makes use of node's undici package and sets fetch and other features from it as global variables. As this is expected to come out officially in node 18 later this year, I recommend that we switch from axios to undici for all fetch requests. If the maintainers agree with this change I would be willing to open the PR that introduces that.

translationsApi.buildProject ignores parameters and just downloads latest build

I'm using crowdin-api-client version 1.9.0.

const buildRequestResult = await crowdinAccess.translationsApi.buildProject(
            kCrowdinProjectId,
            {
                exportApprovedOnly: false, 
                skipUntranslatedStrings: false,
            }
        );
console.log(JSON.stringify(buildRequestResult, null, 4));

If a build is already available, this returns that build, ignoring the arguments in my BuildRequest. For example, right now the above is returning

{
    "data": {
        "id": 24,
        "projectId": 261564,
        "status": "finished",
        "progress": 100,
        "attributes": {
            "branchId": null,
            "targetLanguageIds": [],
            "skipUntranslatedStrings": true, <--- not what I asked for
            "skipUntranslatedFiles": false,
            "exportApprovedOnly": true  <--- not what I asked for
        }
    }
}

If I do something on Crowdin.com to cause the build to become out of date, then the next time I run the above, it starts a new build and when it is done, I get the parameters I was asking for. Is there a call I can make to force a new build when I change the parameters?

GNU

chrome-distiller://2c0be80d-e5cb-4f9d-9ad1-3d72227a2180_de6ad93247a230656cc1e5ce68158e0250b571b6778eb2873fc8eaf73c40fc0f/?title=Preguntas+frecuentes+acerca+de+las+licencias+de+GNU+-+Proyecto+GNU+-+Free+Software+Foundation&url=https%3A%2F%2Fwww.gnu.org%2Flicenses%2Fgpl-faq.html

Issues Data Model difficulties

The api is very explicit, but its data model is difficult to work with extensively.

The issues api has been deprecated and instead the StringComments is offered.
StringComments requires a stringId, which can be obtained by the Strings Api.
However, the strings api returns all strings of a project, which can be many, and there is no way to filter them out, to get the relevant strings of which have comments that are of type issue. I must call N queries for the strings, and then check for each query if has issues, instead of directly check what issues exists (and what strings are related)

How is expected to obtain the issues of strings?

Translations API group buildProject method returns error

When I try to call translationsApi.buildProject(projectId) method it returns following error:
{ error: { message: 'Unsupported Content-Type Header', code: 415 } }

According to the documentation the Content-Type Header should be set to application/json

How can I customise the request with the proper Content-Type Header?

This is how I create the instance:

const { Translations } = require('@crowdin/crowdin-api-client');

const translationsApi = new Translations(
      {
        token,
      },
      {
        headers: {
          'Content-Type': 'application/json',
        },
      },
    );

This approach doesn't work.

When I checked the source code I didn't see the way to extend the defaultConfig that's used under the hood.

Libjpeg-turbo

chrome-distiller://1b21d6c9-0eed-4100-9c25-5f9d7c0df69e_44fb6663b51c153ef8e0549fb5c421c4964abdcdd8ba038b97172c065043a656/?title=C%C3%B3digo+legal+de+Creative+Commons&url=https%3A%2F%2Fcreativecommons.org%2Flicenses%2Fby%2F2.5%2Flegalcode

Can't upload screenshot mimeTypeInvalid

I'm trying to upload a screenshot using the client from a nodejs backend.
My front-end is sending a file to my nodejs backend that is using multer and express to fetch it with the following endpoint

app.post(
  "/api/upload-screenshot",
  upload.single("file"),
  async function (req, res) {
    const file = req.file;

    console.log(file);

    let resUpload = await uploadStorageApi.addStorage(file.originalname, file, file.mimetype);

    const createdStorageId = resUpload.data.id;
    await screenshotsApi.addScreenshot(projectId, {
      storageId: createdStorageId,
      name: file.originalname,
      autoTag: false,
    })

    res.sendStatus(200);
  }
);

I get the following print from the console.log

{
  fieldname: 'file',
  originalname: 'my-image.png',
  encoding: '7bit',
  mimetype: 'image/png',
  destination: 'C:\\Users\\xxxx\\AppData\\Local\\Temp',
  filename: '92ad61f0e5ac2e4396db2ab314207c50',
  path: 'C:\\Users\\xxxx\\AppData\\Local\\Temp\\92ad61f0e5ac2e4396db2ab314207c50',
  size: 103697
}

But still crowdin returns the answer:

{"code":400,"validationCodes":[{"key":"storageId","codes":["mimeTypeInvalid"]}]}

When I call getStorage I can see that my file is there {"data":{"id":132,"fileName":"my-image.png"}}

I've tried with multiple files, I've even tried to make it work with axios but I always come to the same reult could you tell me what I'm doing wrong ?


Client version version 1.19.2
System: Windows 10

How to upload a new revision of the file?

Hi. I'm trying to migrate my script for uploading/downloading strings and I faced the problem, that there is no way to attach a file to upload it to the Crowdin. Previously I have used API client library for v1 and it was worked fine. Now I need the same method for v2.

But the problem is that even in the documentation there is no mention about how to attach the file to the request. This library also allows only to specify the import/export params and some storageId, which I have no idea why.

Can you help me, please?

HttpClientType.FETCH doesn't work

Specifying the httpClientType as Fetch and making an API call seems to throw an error internally.
Code:

const config = {
  projectID: 000000,
  token: "my personal access token"
}
const Crowdin = require("@crowdin/crowdin-api-client");
const translationsAPI = new (Crowdin.Translations)({
  token: config.token
}, {
  httpClientType: Crowdin.HttpClientType.FETCH
});
// Converts error to an Error object if not already
const formatError = error => error instanceof Error ? error : new Error(`${error.error.code} ${error.error.message}`)

translationsAPI.listProjectBuilds(config.projectID)
  .then(console.log, error => Promise.reject(formatError(error)))

Error:

ReferenceError: fetch is not defined
    at FetchClient.<anonymous> (./node_modules/@crowdin/crowdin-api-client/out/core/internal/fetch/fetchClient.js:52:13)
    at Generator.next (<anonymous>)
    at fulfilled (./node_modules/@crowdin/crowdin-api-client/out/core/internal/fetch/fetchClient.js:5:58)

Looking at the said file out/core/internal/fetch/fetchClient.js, it is indeed true that fetch isn't defined. Adding in const fetch = require("node-fetch") at the top of the file seems to resolve the issue. I like to use node-fetch API due to its compliance with the fetch API and I already use it, so I wouldn't need to have Axios installed. I did want to make a PR to add this, but I wanted to get your opinion on this first in case I overlooked something.

autoTag on existing screenshots is not supported

Using the API (https://developer.crowdin.com/api/v2/#operation/api.projects.screenshots.tags.putMany) it is possible to autoTag an existing image by providing the body:

{
  "autoTag": true
}

However, the js client only supports passing the list of tags that should be added (https://github.com/crowdin/crowdin-api-client-js/blob/master/src/screenshots/index.ts#L147)

    replaceTags(projectId: number, screenshotId: number, request: ScreenshotsModel.AddTagRequest[]): Promise<void> {
        const url = `${this.url}/projects/${projectId}/screenshots/${screenshotId}/tags`;
        return this.put(url, request, this.defaultConfig());
    }

The signature of the replaceTags method should be updated so that autoTag is also supported.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.