npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

raven-rookery

v1.0.0

Published

raven-rookery is a backend tool to get localizations from the popular POEditor localization service. raven-rookery gets and stores your localizations in memory, refreshing them at a given interval. It also provides an optional customizable API.

Downloads

2

Readme

raven-rookery

raven-rookery is a backend tool to get localizations from the popular POEditor localization service. raven-rookery gets and stores your localizations in memory, refreshing them at a given interval. It also provides an optional customizable API.

You no longer need to worry about throttling, queuing and rate limiting. Instead of making calls to POEditor's API, incorporate raven-rookery in your current project's API or use its built in API.

The API response from POEditor has a bunch of info you probably don't need. Additionally all languages in a project require their own API call. raven-rookery provides a much cleaner response: just key/value pairs describing your content and you can choose which languages you'd like in one call.

Here's a response hitting POEditor's API directly:

{
  "response": {
    "status": "success",
    "code": "200",
    "message": "OK"
  },
  "result": {
    "terms": [
      {
        "term": "GREETING",
        "context": "",
        "plural": "",
        "created": "2013-06-10T11:08:54+0000",
        "updated": "",
        "translation": {
          "content": "Hello",
          "fuzzy": 0,
          "proofread": 1,
          "updated": "2013-06-12T11:08:54+0000"
        },
        "reference": "",
        "tags": [
          "first_upload",
          "second_upload"
        ],
        "comment": ""
      }
      {}
    ]
  }
}

Here's a response using raven-rookery:

{
  "en-us": {
    "GREETING": "Hello",
    "ETC": "..."
  },
  "sp-mx": {
    "GREETING": "Hola",
    "ETC": "..."
  }
}

If you find raven-rockery useful consider using raven-writer on the FE to incorporate markdown and interpolations.

Additionally, raven-rockery is instantiated with your read-only API token once, instead of being used with every API call if hitting POEditor directly. This lessens the risk of it being committed accidentally if you have a larger team or codebase.


Docs

Install

$ npm install raven-rookery -S

Quick start example with starting server

This will start a dedicated express app. This is not required though. See the next example if you want to start your own server and integrate raven-rookery's functionality.

import { initRookery } from "raven-rookery";

const { startServer } = await initRookery({
  token: "<YOUR_READ_ONLY_POEDITOR_API_TOKEN>",
  projectIds: ["MY_POEDITOR_PROJECT_ID", "ETC"]
});

startServer();

When running locally and making the following request:

curl --globoff 'http://localhost:3000/localizations?data={"projectId":"MY_POEDITOR_PROJECT_ID"}'

You'll get back your localizations for your POEDITOR project with the project id MY_POEDITOR_PROJECT_ID.

Quick start example without starting server

In this example you'll be integrating raven-rookery with an already existing express app you've spun up yourself.

Note that of course you'd be setting your endpoint and query params to whatever you'd like.

import express from "express";
import { initRookery } from "./InitRookery/index.js";

const { getLocalizations } = await initRookery({
  token: "<YOUR_READ_ONLY_POEDITOR_API_TOKEN>",
  projectIds: ["MY_POEDITOR_PROJECT_ID", "ETC"]
});

const app = express();
app.use(express.json());

app.get("/some-endpoint", (req, res) => {
  const query = req.query["my-query"];
  const { id, langs } = JSON.parse(query);
  res.json(getLocalizations(id, langs));
});

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

When running locally and making the following request:

curl --globoff 'http://localhost:3000/some-endpoint?my-query={"id":"MY_POEDITOR_PROJECT_ID","langs":["en-us"]}'

You'll get back your localizations for your POEDITOR project with the project id MY_POEDITOR_PROJECT_ID and for just the language en-us.

initRookery({...})

Gets localizations for a set of POEditor projects, stores them in memory, and refreshes their values per a set amount of time. Returns functions to access those localizations and to start up an express server to access the localizations through a built in API.

Props

| Prop | Type | Required? | Description | | -----------------| :-------------: | :-------: | ----------- | | token | String | ✅ | The read only api_token found in your POEditor acct | | projectIds | Array[String] | ✅ | An array of project ids found in your POEditor acct | | keepAlive | Integer | | The refresh frequency in ms to GET localizations from POEditor. Defaults to 600000 (10 minutes) | | serverConfig | Object | | Configurations for the server created with startServer(). See the serverConfig table below. |

serverConfig prop

| Key | Type | Default value | Description | | ---------------- | :-------------: | :----------------: | ------------ | | endpoint | String | "/localizations" | The endpoint to request localizations. | | port | Integer | 3000 | The port that the express server will run on | | requestType | String | "GET" | Can be "POST" OR "GET" | | dataKey | String | "data" | Changes the query param | | languagesKey | String | "languages" | Changes the languages key in the request | | projectIdKey | String | "projectId" | Changes the project id key in the request |

Please see: Customizing API endpoint and params for info on changing serverConfig values and general info on making calls.

Returns

A promise that resolves to an object holding three methods:

Usage

See also quick start examples.

The following code snippet gets localization for projects w/ IDs "ID_1" and "ID_2". It will refresh the values of those localizations every 5 minutes. It sets some localizations of the projects into two consts. It starts an express server on port 3000 and will be accessible via the endpoint /api/translations for POST requests.

import { initRookery } from "raven-rookery";

const {
  getLocalizations,
  getLocalizationsByLang,
  startServer,
  killServer
} = await initRookery({
  token: "<YOUR_READ_ONLY_POEDITOR_API_TOKEN>",
  projectIds: ["ID_1", "ID_2"],
  keepAlive: 1000 * 60 * 5,
  serverConfig: {
    endPoint: "/api/translations",
    requestType: "POST"
  }
});

const allLocalizationsForProject1 = getLocalizations("ID_1");
const enUSLocalizationsForProject2 = getLocalizationsByLang("ID_2", "en-us");

startServer();

getLocalizations(id, languages)

Gets localizations for a given POEditor project whose id was used to instantiate initRookery({...}), optionally filtered by an array of languages present on that project.

Props

| Prop | Type | Required? | Description | | --------------| :-------------: | :-------: | ----------- | | id | String | ✅ | The project id of one of the projects used to instantiate initRookery({...}) | | languages | Array[String] | | Array of ISO 639-1 language codes that exist in the project whose id you passed in. If missing, all language localizations are returned. |

Returns

A nest object whose top level keys are ISO 639-1 language codes, each holding that language's localizations in key/value pairs where the key is what POEditor refers to as the term and value is what POEditor refers to as the content. See the POEditor API for more details.

{
  "en-us": {
    "GREETING": "Hello"
  },
  "en-au": {
    "GREETING": "G'day"
  },
  "sp-mx": {
    "GREETING": "Hola"
  }
}

Usage

import { initRookery } from "raven-rookery";

const { getLocalizations } = await initRookery({
  token: "<YOUR_READ_ONLY_POEDITOR_API_TOKEN>",
  projectIds: ["ID_1", "ID_2"]
});

console.log(getLocalizations("ID_1", ["en-us"]));
/*
Logs:
"en-us": {
  "GREETING": "Hello"
}
*/

console.log(getLocalizations("ID_1"))
// logs all localizations for en-us, en-au, and sp-mx

getLocalizationsByLang(id, languages)

Gets localizations of a language for a given POEditor project whose id was used to instantiate initRookery({...}). The language given must be present on the POEditor project whose id is passed here.

Props

| Prop | Type | Required? | Description | | -------------| :------: | :-------: | ----------- | | id | String | ✅ | The project id of one of the projects used to instantiate initRookery({...}) | | language | String | ✅ | A ISO 639-1 language code that exists in the project whose id you passed in. |

Returns

A flat object with key/value pairs where the key is what POEditor refers to as the term and value is what POEditor refers to as the content. See the POEditor API for more details.

{
  "GREETING": "Hello"
}

Usage

import { initRookery } from "raven-rookery";

const { getLocalizationsByLang } = await initRookery({
  token: "<YOUR_READ_ONLY_POEDITOR_API_TOKEN>",
  projectIds: ["ID_1", "ID_2"]
});

console.log(getLocalizationsByLang("ID_1", "en-us"));
/*
Logs:
{
  "GREETING": "Hello",
  ...
}
*/

console.log(getLocalizationsByLang("ID_1", "sp-mx"));
/*
Logs:
{
  "GREETING": "Hola",
  ...
}
*/

startServer()

Starts an express server with a built in API to get localizations from endpoints. Out of the box it will listen for GET requests at the /localizations but this and other aspects can be customized. Please take a look customizing API endpoint and params.

Props

none

Returns

An object with keys app holding the created express app and server a UNIX socket. You don't have to do anything with these but they are there if you need them.

  { app, server }

Usage

import { initRookery } from "raven-rookery";

const { startServer } = await initRookery({...});

startServer();

/*
const { app, server } = startServer();
if you wanted to access the app and server.
*/

Once this is running you can make API calls to get localizations.

killServer()

Kills the express server started with startServer().

Props

none

Returns

undefined

Usage

import { initRookery } from "raven-rookery";

const { startServer } = await initRookery({...});

startServer();
killServer();

Once called you can no longer make API calls to get localizations.

Customizing API endpoint and params

The following sections outlines how to use the serverConfig object's dataKey, languagesKey, and projectIdKey to customize how you call API endpoints created via startServer(). They are all optional. What follows are comparisons to illustrate their use.

Please refer to Response schema

GET

Note: to construct the URL I recommend using JSON.stringify():

const options = {
  projectId: "ID_1",
  languages: ["en-us"]
};

const url = `'http://localhost:3000/localizations?data=${JSON.stringify(options)}`;
// with all defaults
const { startServer } = await initRookery({token: "MY_TOKEN", projectIds: ["ID_1", "ID_2"]});
startServer();

You'd need to make the following GET request for a single project, returning a 2-D response:

curl --globoff 'http://localhost:3000/localizations?data={"projectId":"ID_1","languages":["en-us"]}'

You'd need to make the following GET request for a multiple projects, returning a 3-D response:

curl --globoff 'http://localhost:3000/localizations?data=[{"projectId":"ID_1","languages":["en-us"]},{"projectId":"ID_1","languages":["en-us"]}]'

Now if we adjust the endpoint, port, dataKey, projectIdKey and languagesKey values:

const { startServer } = await initRookery({
  token: "MY_TOKEN",
  projectIds: ["ID_1", "ID_2"],
  serverConfig: {
    endpoint: "/my/long/endpoint",
    port: 1234,
    dataKey: "query",
    projectIdKey: "id",
    languagesKey: "langs"
  }
});
startServer();

You'd need to make the following GET request to get the same result 2-D response as the previous call:

curl --globoff 'http://localhost:1234/my/long/endpoint?query={"id":"ID_1","langs":["en-us"]}'

Or the following GET to get the same 3-D response as the previous call:

curl --globoff 'http://localhost:1234/my/long/endpoint?query=[{"id":"ID_1","langs":["en-us"]},{"id":"ID_2","langs":["en-us"]}]'

POST

// with all defaults except for requestType being set to "POST"
const { startServer } = await initRookery({
  token: "MY_TOKEN",
  projectIds: ["ID_1", "ID_2"],
  serverConfig: {
    requestType: "POST"
  }
});
startServer();

You'd need to make the following POST request for a single project, returning a 2-D response:

curl --location 'http://localhost:3000/localizations' \
  --header 'Content-Type: application/json' \
  --data '{ "projectId": "ID_1", "languages":["en-us"] }'

You'd need to make the following POST request for a multiple projects, returning a 3-D response:

curl --location 'http://localhost:3000/localizations' \
  --header 'Content-Type: application/json' \
  --data '{ "data": [{"projectId": "ID_1","languages":["en-us"]}, {"projectId": "ID_2","languages":["en-us"]}] }'

Now if we adjust the endpoint, port, dataKey, projectIdKey and languagesKey values:

const { startServer } = await initRookery({
  token: "MY_TOKEN",
  projectIds: ["ID_1", "ID_2"],
  serverConfig: {
    endpoint: "/my/long/endpoint",
    port: 1234,
    dataKey: "query",
    projectIdKey: "id",
    languagesKey: "langs"
  }
});
startServer();

You'd need to make the following POST request to get the same result 2-D response as the previous call:

curl --location 'http://localhost:1234/my/long/endpoint' \
  --header 'Content-Type: application/json' \
  --data '{ "id": "ID_1", "langs":["en-us"] }'

Or the following POST to get the same 3-D response as the previous call:

curl --location 'http://localhost:1234/my/long/endpoint' \
  --header 'Content-Type: application/json' \
  --data '{ "query": [{"id": "ID_1","langs":["en-us"]}, {"id": "ID_2","langs":["en-us"]}] }'

Response schema

The response is a nested object, either 2 or 3 dimensions depending on your request. Both GET and POST calls return the same response. To enable POST requests see initRookery({...}).

2-D API calls

The localizations for one project are returned as a single nested object. Top level keys are ISO 639-1 language codes, each holding that language's localizations in key/value pairs where the key is what POEditor refers to as the term and value is what POEditor refers to as the content. See the POEditor API for more details.

Note to get a 2-D response you're passing an object, to get a 3-D response you're passing an array of objects.

GET

curl --globoff 'http://localhost:3000/localizations?data={"projectId":"MY_POEDITOR_PROJECT_ID"}'

(Note: This GET call integrates well with the raven-writer front end util. To do so, set the url in the POE.fetchLocalizations({...}) method.)

POST

curl --location 'http://localhost:3000/localizations' \
  --header 'Content-Type: application/json' \
  --data '{ "projectId": "MY_POEDITOR_PROJECT_ID" }'

Response

Assuming you have the following languages on your project: en-us, sp-mx and a POE term GREETING.

{
  "en-us": {
    "GREETING": "Hello"
  },
  "sp-mx": {
    "GREETING": "Hola"
  }
}

3-D API calls

Returns one or more projects in a single nested object with the given project ID as the key for each.

GET

curl --globoff 'http://localhost:3000/localizations?data=[{"projectId":"MY_POEDITOR_PROJECT_ID"}]'

POST

curl --location 'http://localhost:3000/localizations' \
  --header 'Content-Type: application/json' \
  --data '{ "data": [{"projectId": "MY_POEDITOR_PROJECT_ID"}] }'

Response

Assuming you have the following languages on your project with id MY_POEDITOR_PROJECT_ID: en-us, sp-mx and a POE term GREETING and

{
  "MY_POEDITOR_PROJECT_ID": {
    "en-us": {
      "GREETING": "Hello"
    },
    "sp-mx": {
      "GREETING": "Hola"
    }
  }
}