Lawrence  Lesch

Lawrence Lesch


A Fully Type-safe and Lightweight internationalization Library

🌍 typesafe-i18n

A fully type-safe and lightweight internationalization library for all your TypeScript and JavaScript projects.


🐤 lightweight (~1kb)
👌 easy to use syntax
🏃 fast and efficient
🦺 prevents you from making mistakes (also in plain JavaScript projects)
👷 creates boilerplate code for you
💬 supports plural rules
📅 allows formatting of values e.g. locale-dependent date or number formats
↔️ supports switch-case statements e.g. for gender-specific output
⬇️ option for asynchronous loading of locales
📚 supports multiple namespaces
⏱️ supports SSR (Server-Side Rendering)
🤝 can be used for frontend, backend and API projects
🔍 locale-detection for browser and server environments
🔄 import and export translations from/to files or services
⛔ no external dependencies

Interactive Live Demo

Click here to see an interactive demo of typesafe-i18n showing some key aspects of the type-checking capabilities of this internationalization library.

Get started

⌨️ Run the setup process and automatically detect the config needed

or manually configure typesafe-i18n by answering a few questions

It didn't work? See here for possible troubleshooting.

npx typesafe-i18n --setup
npx typesafe-i18n --setup-auto

👀 Take a look at the generated files and it's folder-structure after running npm run typesafe-i18n (or npx typesafe-i18n)

📖 Explore the assets

typesafe-i18n offers a lot. Just press cmd + F to search on this page or see the table of contents that will link you to more specific subpages with more details.

⭐ Star this project on GitHub

Thanks! This helps the project to grow.

Having trouble setting up typesafe-i18n? Reach out to us via Github Discussions or on Discord.

manual installation

npm install typesafe-i18n


The changelog of this project can be found here


Long-term goals

Curious about what comes next? See this discussion to learn more about the plans for the future of this project.


If you would like to get involved within this project, take a look at this discussion.


The package can be used inside JavaScript and TypeScript applications. You will get a lot of benefits by running the generator since it will create a few wrappers to provide you with full typesafety.

You can use typesafe-i18n in a variety of project-setups:

Other frameworks

All you need is inside the generated file i18n-util.ts. You can use the functions in there to create a small wrapper for your application.

Feel free to open a new discussion if you need a guide for a specific framework.

Custom usage

See here if you want to learn how you can use typesafe-i18n to implement your own specific use-case.

Browser Support

The library should work in all modern browsers. It uses some functionality from the Intl namespace. You can see the list of supported browsers here. If you want to support older browsers that don't include these functions, you would need to include a polyfill like intl-pluralrules.


If you want to get the best typesafety features, you will need to use the generator in order to create types and boilerplate code for you

Here you can see some examples where typesafe-i18n can help you:

typesafe auto-completion for all your defined locales

typesafe locales completion

typesafe auto-completion for all available translations

typesafe translation key completion

you will get an error if you forget to pass arguments

typesafe number of arguments

you will get an error if you pass the wrong type arguments

typesafe arguments 1 typesafe arguments 2

you will get an error if you forgot to add a translation in a locale

typesafe keys in translations

you will get an error when a translation is missing an argument

typesafe arguments in translation

The typesafe-i18n package allows us to be 100% typesafe for our translation functions and even the translations for other locales itself. The generator outputs TypeScript definitions based on your base locale.

You will also benefit from full typesafe JavaScript code via JSDoc-annotations.

Integration with other services

typesafe-i18n comes with an API that allows other services to read and update translations. You can connect other services by using the importer and exporter functionality.


The footprint of the typesafe-i18n package is smaller compared to other existing i18n packages. Most of the magic happens in development mode, where the generator creates TypeScript definitions for your translations. This means, you don't have to ship the whole package to your users. The only two parts, that are needed in production are:

  • string-parser: detects variables, formatters and plural-rules in your localized strings
  • translation function: injects arguments, formats them and finds the correct plural form for the given arguments

These parts are bundled into the core functions. The sizes of the core functionalities are:

Apart from that there can be a small overhead depending on which utilities and wrappers you use.

There also exists a useful wrapper for some frameworks:


The package was optimized for performance:

  • the amount of network traffic is kept small
    The translation functions are small. Only the locales that are used are loaded
  • no unnecessary workload
    Parsing your translation file for variables and formatters will only be performed when you access a translation for the first time. The result of that parsing process will be stored in an optimized object and kept in memory.
  • fast translations
    Passing variables to the translation function will be fast, because its treated like a simple string concatenation. For formatting values, a single function is called per formatter.

If you use typesafe-i18n you will get a smaller bundle compared to other i18n solutions. But that doesn't mean, we should stop there. There are some possible optimizations planned to decrease the bundle size even further.


Dou you still have some questions? Reach out to us via Github Discussions or on Discord.

Installing typesafe-i18n fails

Running the npx command with a npm version <7.0.0 will probably fail because it will not include peerDependencies.

You could try installing it locally via:

npm install typesafe-i18n

and then run the setup-command from within the node_modules folder via:

./node_modules/typesafe-i18n/cli/typesafe-i18n.mjs --setup-auto

here is the original issue with some additional information: #142

I added a new translation to my locale file, but TypeScript gives me the Error Property 'XYZ' does not exist on type 'TranslationFunctions'

Make sure to run the generator after you make changes to your base translation file. The generator will generate and update the types for you.

I don't use TypeScript, can I also use typesafe-i18n inside JavaScript applications?

Yes, you can. See the usage section for instructions. Even if you don't use TypeScript you can still improve from some typesafety features via JSDoc-annotations.

I added a new translation to my locale file, but the generator will not create new types

The generator will only look for changes in your base locale file. Make sure to always update your base locale file first, in order to get the correct auto-generated types. If you want to change your base locale file, make sure to give it the type of BaseTranslation. All other locales should have the type of Translation. E.g. if you set your base locale to italian, you would need to do it like this:

set your base locale to italian (it) in ´.typesafe-i18n.json`:

   "baseLocale": "it"

define the type of your base locale as BaseTranslation

// file 'src/i18n/it/index.ts'
import type { BaseTranslation } from '../i18n-types'

const it: BaseTranslation = {
   WELCOME: "Benvenuto!"

export default it

define the type of your other locales as Translation

// file 'src/i18n/en/index.ts'
import type { Translation } from '../i18n-types'

const en: Translation = {
   WELCOME: "Welcome!"

export default en

The generator keeps overriding my changes I make to the i18n-files

The generator creates some helpful wrappers for you. If you want to write your own wrappers, you can disable the generation of these files by setting the generateOnlyTypes option to true.

Is typesafe-i18n supported by i18n-ally?

Yes, you can configure i18n-ally like this. There is currently also an open PR that will add official support for typesafe-i18n.

How can I access a translation dynamically?

When you want to dynamically access a translation, you can use the usual JavaScript syntax to access a property via a variable (myObject[myVariable]).

  • define your translations 
// i18n/en.ts
import type { BaseTranslation } from '../i18n-types'

const en: BaseTranslation = {
   category: {
      simple: {
         title: 'Simple title',
         description: 'I am a description for the "simple" category',
      advanced: {
         title: 'Advanced title',
         description: 'I am a description for the "advanced" category',

export default en
  • use it in your components

<script lang="ts">
   // Component.svelte

   import LL from '$i18n/i18n-svelte'
   import type { Translation } from '$i18n/i18n-types'

   // ! do not type it as `string`
   // by restricting the type, you don't loose the typesafety features
   export let category: keyof Translation['category'] = 'simple'



How do I render a component inside a Translation?

By default typesafe-i18n at this time does not provide such a functionality. Basically you will need to write a function that splits the translated message and renders a component between the parts. You can define your split characters yourself but you would always need to make sure you add them in any translation since typesafe-i18n doesn't provide any typesafety for these characters (yet).

How can I use my base translation as a fallback for other locales?

With the strong typesafety features, you'll know if a locale is missing a translation. But in rare cases you might want to use your base translation as a fallback for other locales.

See the next FAQ entry. The same concept can be applied to prefill your translations with the base translation and then just override the parts that are translated.

You'll loose the some sort of typesafety with that approach since you can't know which parts are translated and which are not. Using the base translation as a fallback is not recommended because your UI will contain two different locales which might confuse your users.

I have two similar locales (only a few translations are different) but I don't want to duplicate my translations

Your locale translation files can be any kind of JavaScript object. So you can make object-transformations inside your translation file. The only restriction is: in the end it has to contain a default export with type Translation. You could do something like this:

create your BaseTranslation

// file 'src/i18n/en/index.ts'
import type { BaseTranslation } from '../i18n-types'

const en: BaseTranslation = {
   WELCOME: "Welcome to XYZ",
   // ... some other translations

   COLOR: "colour"

export default en

create your other translation that overrides specific translations

If you are using nested translations, you should use the provided extendDictionary function that uses lodash/merge under the hood.

import { extendDictionary } from '../i18n-utils'
import en from '../en' // import translations from 'en' locale

const en_US = extendDictionary(en, {
   labels: {
      color: "color" // override specific translations

export default en_US
// file 'src/i18n/en-US/index.ts'
import type { Translation } from '../i18n-types'
import en from '../en' // import translations from 'en' locale

const en_US: Translation = {
   ...en as Translation, // use destructuring to copy all translations from your 'en' locale

   COLOR: "color" // override specific translations

export default en_US

For certain locales I don't want to output a variable, but due to the strict typing I have to specify it in my translation

The generated types are really strict. It helps you from making unintentional mistakes. If you want to opt-out for certain translations, you can use the any keyword.

create your BaseTranslation with a translation containing a parameter

// file 'src/i18n/en/index.ts'
import type { BaseTranslation } from '../i18n-types'

const en: BaseTranslation = {
   HELLO: "Hi {name}!",

export default en

create another locale without that parameter by disabling the strict type checking with as any

// file 'src/i18n/de/index.ts'
import type { Translation } from '../i18n-types'

const de: Translation = {
   HELLO: "Hallo!" as any // we don't want to output the 'name' variable

export default de

WARNING! the usage of 'any' can introduce unintentional mistakes in future. It should only be used when really necessary and you know what you are doing.

A better approach would be to create a custom formatter e.g.

create your translation and add a formatter to your variable

// file 'src/i18n/de/index.ts'
import type { Translation } from '../i18n-types'

const de: Translation = {
   HELLO: "Hallo {name|nameFormatter}!"

export default de
// file 'src/i18n/en/index.ts'
import type { BaseTranslation } from '../i18n-types'

const en: BaseTranslation = {
   HELLO: "Hi {name|nameFormatter}!",

export default en

create the formatter based on the locale

// file 'src/i18n/formatters.ts'
import type { FormattersInitializer } from 'typesafe-i18n'
import type { Locales, Formatters } from './i18n-types'
import { identity, ignore } from 'typesafe-i18n/formatters'

export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales)    => {

   const nameFormatter =
      locale === 'de'
         // return an empty string for locale 'de'
         ? ignore // same as: () => ''
         // return the unmodified parameter
         : identity // same as: (value) => value

   const formatters: Formatters = {
      nameFormatter: nameFormatter

   return formatters

Why does the translation function return a type of LocalizedString and not the type string itself?

With the help of LocalizedString you could enforce texts in your application to be translated. Lets take an Error message as example:

const showErrorMessage(message: string) => alert(message)

const createUser = (name: string, password: string) => {
   if (name.length === 0) {

   if (isStrongPassword(password)) {
      showErrorMessage('Password is too weak')

   // ... create user in DB

In this example we can pass in any string, so it can also happen that some parts of your application are not translated. To improve your i18n experience a bit we can take advantage of the LocalizedString type:

import type { LocalizedString } from 'typesafe-i18n'

const showErrorMessage(message: LocalizedString) => alert(message)

const createUser = (name: string, password: string) => {
   if (name.length === 0) {

   if (isStrongPassword(password)) {
      showErrorMessage('Password is too weak') // => ERROR: Argument of type 'string' is not assignable to parameter of type 'LocalizedString'.

   // ... create user in DB

With the type LocalizedString you can restrict your functions to only translated strings.

Tests are not running with Jest

Unfortunately there are some open issues in the Jest repository regarding modern package export formats so jest doesn't know where to load files from.

You need to manually tell jest where these files should be loaded from, by defining moduleNameMapper inside your jest.config.js:

// jest.config.js
module.exports = {
   moduleNameMapper: {
      "typesafe-i18n/angular": "typesafe-i18n/angular/index.cjs",
      "typesafe-i18n/react": "typesafe-i18n/react/index.cjs",
      "typesafe-i18n/solid": "typesafe-i18n/solid/index.cjs",
      "typesafe-i18n/svelte": "typesafe-i18n/svelte/index.cjs",
      "typesafe-i18n/vue": "typesafe-i18n/vue/index.cjs",
      "typesafe-i18n/formatters": "typesafe-i18n/formatters/index.cjs",
      "typesafe-i18n/detectors": "typesafe-i18n/detectors/index.cjs",

here is the original issue with some additional information: #140

With Node.JS the Intl package does not work with locales other than 'en'

Node.JS, by default, does not come with the full intl support. To reduce the size of the node installment it will only include 'en' as locale. You would need to add it yourself. The easiest way is to install the intl package

> npm install intl

and then add following lines on top of your src/i18n/formatters.ts file:

const intl = require('intl')
globalThis.Intl.DateTimeFormat = intl.DateTimeFormat

Then you should be able to use formatters from the Intl namespace with all locales.

Note: this is an older approach to the problem. You should not need this when using Node.js version > 16.


"Cannot find module" in yarn monorepo setup

Yarn uses a strange way to install dependencies in a monorepo setup. The issue lays in the "hoisting" of packages (see this issue). Therefore it might be that the typesafe-i18n dependencies cannot be found.

Changing the workspace config in package.json will fix the issue:

-  "workspaces": [
-    "apps/*",
-    "packages/*"
-  ],
+  "workspaces": {
+    "packages": [
+      "apps/*",
+      "packages/*"
+    ],
+    "nohoist": [
+      "**/typesafe-i18n",
+      "**/typesafe-i18n/**"
+    ]
+  },

Download Details:

Author: ivanhofer
Source Code: 
License: MIT license

#typescript #javascript #react #nodejs #i18n 

A Fully Type-safe and Lightweight internationalization Library
Lawrence  Lesch

Lawrence Lesch


Ever-traduora: Open-Source Translation Management Platform

Ever Traduora Platform

Ever® Traduora - Open Translation Management Platform for teams.

Once you setup your project you can import and export your translations to various formats, work together with your team, instantly deliver translation updates over the air, and soon automatically translate your project via third-party integrations.

Traduora Product Screenshot

We want Traduora to become the home for managing your translation workflow, that's why we have made all of the core product open-source with the intention to grow a community and enable developers to build on top of it as a platform.

We are going to also use Traduora from our other open-source platforms (currently and You are welcome to check more information about the platforms at our official website -


For quick features review, please see our official docs screenshots page.

Short list of platform features:

  • 5-minute setup with Docker, Kubernetes or from source
  • Find what you are looking for with instant search
  • Invite your team, everyone can work together on the same project
  • Automate your translation workflow via our REST API
  • Import and export to your favorite formats: JSON flat and nested, CSV, YAML flat and nested, Java Properties, XLIFF 1.2, Gettext (po), Strings, Android Resources (xml).
  • community contributed CLI available at (not official CLI)

For more information check out our official website, or our docs at

Any missing feature you'd like to see? File an issue with the feature request to let us know.

Try it out

Traduora can be run just about anywhere, check out our Quickstart for more info.

Also check out Traduora's Docker Hub page for pre-built images.

Configuration and Deployment

Please check out the configuration and deployment documents for more information on deploying Traduora.

Frequently Asked Questions

Some questions come up over and over again. Be sure to check out our FAQ first!

Contact Us


Security is very important to us. Ever® Traduora Platform follows good security practices, but 100% security cannot be guaranteed in any software! Ever® Traduora Platform is provided AS IS without any warranty. Use at your own risk! See more details in the LICENSE.

In a production setup, all client-side to server-side (backend, APIs) communications should be encrypted using HTTPS/SSL (REST APIs).

If you discover any issue regarding security, please disclose the information responsibly by sending an email to or on huntr and not by creating a GitHub issue.


We think it's great that you'd like to contribute to Traduora.

  • Please give us :star: on Github, it helps!
  • You are more than welcome to submit feature requests in the separate repo.
  • Pull requests are always welcome! Please base pull requests against the develop branch and follow the contribution guidelines.


View full list of our contributors.


Of course we'd like Traduora to be available in as many languages as possible. We're setting up a Traduora server for translating Traduora itself, check back soon for more details on how to contribute.


You can check our changelog for information about releases.


Ever® is a registered trademark of Ever Co. LTD.
Ever® Demand™, Ever® Gauzy™ and Ever® OpenSaaS™ are all trademarks of Ever Co. LTD. The trademarks may only be used with the written permission of Ever Co. LTD. and may not be used to promote or otherwise market competitive products or services.

All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.

Download Details:

Author: ever-co
Source Code: 
License: AGPL-3.0 license

#typescript #nodejs #i18n #api #angular #opensource 

Ever-traduora: Open-Source Translation Management Platform Scheduling infrastructure for Absolutely Everyone (formerly Calendso)

The open-source Calendly alternative.

About The Project


Scheduling infrastructure for absolutely everyone

The open source Calendly alternative. You are in charge of your own data, workflow and appearance.

Calendly and other scheduling tools are awesome. It made our lives massively easier. We're using it for business meetings, seminars, yoga classes and even calls with our families. However, most tools are very limited in terms of control and customisations.

That's where comes in. Self-hosted or hosted by us. White-label by design. API-driven and ready to be deployed on your own domain. Full control of your events and data.


Hacker News

Featured on Hacker News

 Featured on Hacker News

Product Hunt - The open source Calendly alternative | Product Hunt - The open source Calendly alternative | Product Hunt - The open source Calendly alternative | Product Hunt

Built With

Stay Up-to-Date

Cal officially launched as v.1.0 on 15th of September, however a lot of new features are coming. Watch releases of this repository to be notified for future updates:


Getting Started

To get a local copy up and running, please follow these simple steps.


Here is what you need to be able to run Cal.

  • Node.js (Version: >=15.x <17)
  • PostgreSQL
  • Yarn (recommended)

If you want to enable any of the available integrations, you may want to obtain additional credentials for each one. More details on this can be found below under the integrations section.



Clone the repo into a public GitHub repository (or fork If you plan to distribute the code, keep the source code public to comply with AGPLv3. To clone in a private repository, acquire a commercial license)

git clone

Go to the project folder


Install packages with yarn


Set up your .env file

  • Duplicate .env.example to .env
  • Use openssl rand -base64 32 to generate a key and add it under NEXTAUTH_SECRET in the .env file.
  • Use openssl rand -base64 24 to generate a key and add it under CALENDSO_ENCRYPTION_KEY in the .env file.

Quick start with yarn dx

  • Requires Docker and Docker Compose to be installed
  • Will start a local Postgres instance with a few test users - the credentials will be logged in the console
yarn dx

Development tip

Add NEXT_PUBLIC_DEBUG=1 anywhere in your .env to get logging information for all the queries and mutations driven by trpc.

echo 'NEXT_PUBLIC_DEBUG=1' >> .env

Manual setup

Configure environment variables in the .env file. Replace <user>, <pass>, <db-host>, <db-port> with their applicable values

  1. If you don't know how to configure the DATABASE_URL, then follow the steps here to create a quick DB using Heroku

Set a 32 character random string in your .env file for the CALENDSO_ENCRYPTION_KEY (You can use a command like openssl rand -base64 24 to generate one).

Set up the database using the Prisma schema (found in packages/prisma/schema.prisma)

yarn workspace @calcom/prisma db-deploy

Run (in development mode)

yarn dev

Setting up your first user

Open Prisma Studio to look at or modify the database content:

yarn db-studio

Click on the User model to add a new user record.

Fill out the fields email, username, password, and set metadata to empty {} (remembering to encrypt your password with BCrypt) and click Save 1 Record to create your first user.

New users are set on a TRIAL plan by default. You might want to adjust this behavior to your needs in the packages/prisma/schema.prisma file.

Open a browser to http://localhost:3000 and login with your just created, first user.


Be sure to set the environment variable NEXTAUTH_URL to the correct value. If you are running locally, as the documentation within .env.example mentions, the value should be http://localhost:3000.

# In a terminal just run:
yarn test-e2e

# To open last HTML report run:
yarn playwright show-report test-results/reports/playwright-html-report

Upgrading from earlier versions

Pull the current version:

git pull

Check if dependencies got added/updated/removed


Apply database migrations by running one of the following commands:

In a development environment, run:

yarn workspace @calcom/prisma db-migrate

(this can clear your development database in some cases)

In a production environment, run:

yarn workspace @calcom/prisma db-deploy

Check for .env variables changes

yarn predev

Start the server. In a development environment, just do:

yarn dev

For a production build, run for example:

yarn build
yarn start

Enjoy the new version.



The Docker configuration for Cal is an effort powered by people within the community.

If you want to contribute to the Docker repository, reply here.

The Docker configuration can be found in our docker repository.

Issues with Docker? Find your answer or open a new discussion here to ask the community., Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk.




Deploy on Railway

You can deploy Cal on Railway using the button above. The team at Railway also have a detailed blog post on deploying Cal on their platform.


Currently Vercel Pro Plan is required to be able to Deploy this application with Vercel, due to limitations on the number of serverless functions on the free plan.

Deploy with Vercel


Deploy to Render

Roadmap Roadmap

See the roadmap project for a list of proposed features (and known issues). You can change the view to see planned tagged releases.

Repo Activity


Please see our contributing guide.

Good First Issues

We have a list of help wanted that contain small features and bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.


Don't code but still want to contribute? Join our slack and join the #i18n channel and let us know what language you want to translate.

ar translation bg translation cs translation de translation el translation en translation es translation es-419 translation fr translation he translation hu translation it translation ja translation ko translation nl translation no translation pl translation pt translation pt-BR translation ro translation ru translation sr translation sv translation tr translation uk translation vi translation zh-CN translation zh-TW translation


Obtaining the Google API Credentials

  1. Open Google API Console. If you don't have a project in your Google Cloud subscription, you'll need to create one before proceeding further. Under Dashboard pane, select Enable APIS and Services.
  2. In the search box, type calendar and select the Google Calendar API search result.
  3. Enable the selected API.
  4. Next, go to the OAuth consent screen from the side pane. Select the app type (Internal or External) and enter the basic app details on the first page.
  5. In the second page on Scopes, select Add or Remove Scopes. Search for Calendar.event and select the scope with scope value .../auth/, .../auth/calendar.readonly and select Update.
  6. In the third page (Test Users), add the Google account(s) you'll using. Make sure the details are correct on the last page of the wizard and your consent screen will be configured.
  7. Now select Credentials from the side pane and then select Create Credentials. Select the OAuth Client ID option.
  8. Select Web Application as the Application Type.
  9. Under Authorized redirect URI's, select Add URI and then add the URI < URL>/api/integrations/googlecalendar/callback and < URL>/api/auth/callback/google replacing URL with the URI at which your application runs.
  10. The key will be created and you will be redirected back to the Credentials page. Select the newly generated client ID under OAuth 2.0 Client IDs.
  11. Select Download JSON. Copy the contents of this file and paste the entire JSON string in the .env file as the value for GOOGLE_API_CREDENTIALS key.

Adding google calendar to App Store

After adding Google credentials, you can now Google Calendar App to the app store. You can repopulate the App store by running

cd packages/prisma
yarn seed-app-store

You will need to complete a few more steps to activate Google Calendar App. Make sure to complete section "Obtaining the Google API Credentials". After the do the following

  1. Add extra redirect URL < URL>/api/auth/callback/google
  2. Under 'OAuth concent screen', click "PUBLISH APP"

Obtaining Microsoft Graph Client ID and Secret

  1. Open Azure App Registration and select New registration
  2. Name your application
  3. Set Who can use this application or access this API? to Accounts in any organizational directory (Any Azure AD directory - Multitenant)
  4. Set the Web redirect URI to < URL>/api/integrations/office365calendar/callback replacing URL with the URI at which your application runs.
  5. Use Application (client) ID as the MS_GRAPH_CLIENT_ID attribute value in .env
  6. Click Certificates & secrets create a new client secret and use the value as the MS_GRAPH_CLIENT_SECRET attribute

Obtaining Zoom Client ID and Secret

  1. Open Zoom Marketplace and sign in with your Zoom account.
  2. On the upper right, click "Develop" => "Build App".
  3. On "OAuth", select "Create".
  4. Name your App.
  5. Choose "User-managed app" as the app type.
  6. De-select the option to publish the app on the Zoom App Marketplace.
  7. Click "Create".
  8. Now copy the Client ID and Client Secret to your .env file into the ZOOM_CLIENT_ID and ZOOM_CLIENT_SECRET fields.
  9. Set the Redirect URL for OAuth < URL>/api/integrations/zoomvideo/callback replacing URL with the URI at which your application runs.
  10. Also add the redirect URL given above as a allow list URL and enable "Subdomain check". Make sure, it says "saved" below the form.
  11. You don't need to provide basic information about your app. Instead click at "Scopes" and then at "+ Add Scopes". On the left, click the category "Meeting" and check the scope meeting:write.
  12. Click "Done".
  13. You're good to go. Now you can easily add your Zoom integration in the settings.

Obtaining Daily API Credentials

  1. Open Daily and sign into your account.
  2. From within your dashboard, go to the developers tab.
  3. Copy your API key.
  4. Now paste the API key to your .env file into the DAILY_API_KEY field in your .env file.
  5. If you have the Daily Scale Plan set the DAILY_SCALE_PLAN variable to true in order to use features like video recording.

Obtaining HubSpot Client ID and Secret

  1. Open HubSpot Developer and sign into your account, or create a new one.
  2. From within the home of the Developer account page, go to "Manage apps".
  3. Click "Create app" button top right.
  4. Fill in any information you want in the "App info" tab
  5. Go to tab "Auth"
  6. Now copy the Client ID and Client Secret to your .env file into the HUBSPOT_CLIENT_ID and HUBSPOT_CLIENT_SECRET fields.
  7. Set the Redirect URL for OAuth < URL>/api/integrations/hubspot/callback replacing URL with the URI at which your application runs.
  8. In the "Scopes" section at the bottom of the page, make sure you select "Read" and "Write" for scope called crm.objects.contacts
  9. Click the "Save" button at the bottom footer.
  10. You're good to go. Now you can see any booking in created as a meeting in HubSpot for your contacts.


Setting up SendGrid for Email reminders

  1. Create a SendGrid account (
  2. Go to Settings -> API keys and create an API key
  3. Copy API key to your .env file into the SENDGRID_API_KEY field
  4. Go to Settings -> Sender Authentication and verify a single sender
  5. Copy the verified E-Mail to your .env file into the SENDGRID_EMAIL field
  6. Add your custom sender name to the .env file into the NEXT_PUBLIC_SENDGRID_SENDER_NAME field (fallback is

Setting up Twilio for SMS reminders

  1. Create a Twilio account (
  2. Click ‘Get a Twilio phone number’
  3. Copy Account SID to your .env file into the TWILIO_SID field
  4. Copy Auth Token to your .env file into the TWILIO_TOKEN field
  5. Copy your Twilio phone number to your .env file into the TWILIO_PHONE_NUMBER field
  6. Add your own sender id to the .env file into the NEXT_PUBLIC_SENDER_ID field (fallback is Cal)
  7. Create a messaging service (Develop -> Messaging -> Services)
  8. Choose any name for the messaging service
  9. Click 'Add Senders'
  10. Choose phone number as sender type
  11. Add the listed phone number
  12. Leave all other fields as they are
  13. Complete setup and click ‘View my new Messaging Service’
  14. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
  15. Create a verify service
  16. Copy Verify Service SID to your .env file into the TWILIO_VERIFY_SID field


Special thanks to these amazing projects which help power is an open startup and Jitsu (an open-source Segment alternative) helps us to track most of the usage metrics.

Download Details:

Author: Calcom
Source Code: 
License: View license

#opensource #typescript #nextjs #postgresql #prisma #tailwindcss Scheduling infrastructure for Absolutely Everyone
Gordon  Matlala

Gordon Matlala


Polyglot: Multilingual and I18n Support tool for Jekyll Blogs

🔤 Polyglot

Polyglot is a fast, painless, open-source internationalization plugin for Jekyll blogs. Polyglot is easy to setup and use with any Jekyll project, and it scales to the languages you want to support. With fallback support for missing content, automatic url relativization, and powerful SEO tools, Polyglot allows any multi-language jekyll blog to focus on content without the cruft.


Jekyll doesn't provide native support for multi-language blogs. This plugin was modeled after the jekyll-multiple-languages-plugin, whose implementation I liked, but execution I didn't.


Add jekyll-polyglot to your Gemfile if you are using Bundler:

group :jekyll_plugins do
   gem "jekyll-polyglot"

Or install the gem manually by doing gem install jekyll-polyglot and specify the plugin using _config.yml:

  - jekyll-polyglot


In your _config.yml file, add the following preferences

languages: ["en", "sv", "de", "fr"]
default_lang: "en"
exclude_from_localization: ["javascript", "images", "css", "public"]
parallel_localization: true

These configuration preferences indicate

  • what i18n languages you wish to support
  • what is your default "fallback" language for your content
  • what root level files/folders are excluded from localization, based on if their paths start with any of the excluded regexp substrings. (this is different than the jekyll exclude: [ .gitignore ] ; you should exclude files and directories in your repo you dont want in your built site at all, and exclude_from_localization files and directories you want to see in your built site, but not in your sublanguage sites.)
  • whether to run language processing in parallel or serial

The optional lang_from_path: true option enables getting page language from the first or second path segment, e.g de/, or _posts/zh_Hans_HK/ , if the lang frontmatter isn't defined.

How To Use It

When adding new posts and pages, add to the YAML front matter:

lang: sv

or whatever appropriate I18n language code the page should build for. And you're done. Ideally, when designing your site, you should organize files by their relative urls.

Polyglot works by associating documents with similar permalinks to the lang specified in their frontmatter. Files that correspond to similar routes should have identical permalinks. If you don't provide a permalink for a post, make sure you are consistent with how you place and name corresponding files:


Organized names will generate consistent permalinks when the post is rendered, and polyglot will know to build seperate language versions of the website using only the files with the correct lang variable in the front matter.

In short:

  • Be consistent with how you name and place your posts files
  • Always give your pages permalinks in the frontmatter
  • Don't overthink it, :wink:

Fallback Language Support

Lets say you are building your website. You have an /about/ page written in english, german and swedish. You are also supporting a french website, but you never designed a french version of your /about/ page!

No worries. Polyglot ensures the sitemap of your english site matches your french site, matches your swedish and german sites too. In this case, because you specified a default_lang variable in your _config.yml, all sites missing their languages' counterparts will fallback to your default_lang, so content is preserved across different languages of your site.

Relativized Local Urls

No need to meticulously manage anchor tags to link to your correct language. Polyglot modifies how pages get written to the site so your french links keep vistors on your french blog.

title: au sujet de notre entreprise
permalink: /about/
lang: fr
Nous sommes un restaurant situé à Paris . [Ceci est notre menu.](/menu/)


<header class="post-header">
  <h1 class="post-title">au sujet de notre entreprise</h1>

<article class="post-content">
  <p>Nous sommes un restaurant situé à Paris . <a href="/fr/menu/">Ceci est notre menu.</a></p>

Notice the link <a href="/fr/menu/">... directs to the french website.

Even if you are falling back to default_lang page, relative links built on the french site will still link to french pages.

Relativized Absolute Urls

If you defined a site url in your _config.yaml, polyglot will automatically relativize absolute links pointing to your website directory:

lang: fr
Cliquez [ici]({{site.url}}) pour aller à l'entrée du site.


<p>Cliquez <a href="">ici</a> pour aller à l'entrée du site.

Disabling Url Relativizing

New in 1.4.0 If you dont want a href attribute to be relativized (such as for making a language switcher), you can use the block tag:

{% static_href %}href="..."{% endstatic_href %}
<a {% static_href %}href="/about"{% endstatic_href %}>click this static link</a>

that will generate <a href="/about">click this static link</a> which is what you would normally use to create a url unmangled by invisible language relativization.

Combine with a html minifier for a polished and production ready website.

Exclusive site language generation

New in 1.4.0

If you want to control which languages a document can be generated for, you can specify lang-exclusive: [ ] frontmatter. If you include this frontmatter in your post, it will only generate for the specified site languages.

For Example, the following frontmatter will only generate in the en and fr site language builds:

lang-exclusive: ['en', 'fr']

Machine-aware site building

New in 1.5.0

Polyglot will only start builds after it confirms there is a cpu core ready to accept the build thread. This ensures that jekyll will build large sites efficiently streamlining build processes instead of overloading machines with process thrash.


There are cases when localization is required. For instance: you might need to localize _data/navigation.yml that holds "navigation menu". In order to localize it, just place language-specific files in _data/:lang/... folder, and Polyglot will bring those keys to the top level.

How It Works

This plugin makes modifications to existing Jekyll classes and modules, namely Jekyll::StaticFile and Jekyll::Site. These changes are as lightweight and slim as possible. The biggest change is in Jekyll::Site.process. Polyglot overwrites this method to instead spawn a separate process for each language you intend to process the site for. Each of those processes calls the original Jekyll::Site.process method with its language in mind, ensuring your website scales to support any number of languages, while building all of your site languages simultaneously.

Jekyll::Site.process is the entry point for the Jekyll build process. Take care whatever other plugins you use do not also attempt to overwrite this method. You may have problems.


This plugin stands out from other I18n Jekyll plugins.

  • automatically corrects your relative links, keeping your french visitors on your french website, even when content has to fallback to the default_lang.
  • builds all versions of your website simultaneously, allowing big websites to scale efficiently.
  • provides the liquid tag {{ site.languages }} to get an array of your I18n strings.
  • provides the liquid tag {{ site.default_lang }} to get the default_lang I18n string.
  • provides the liquid tag {{ site.active_lang }} to get the I18n language string the website was built for. Alternative names for active_lang can be configured via config.lang_vars.
  • provides the liquid tag {{ I18n_Headers }} to append SEO bonuses to your website.
  • provides the liquid tag {{ Unrelativized_Link href="/hello" }} to make urls that do not get influenced by url correction regexes.
  • provides localization for efficient rich text replacement.
  • a creator that will answer all of your questions and issues.

SEO Recipes

Jekyll-polyglot has a few spectacular Search Engine Optimization techniques to ensure your Jekyll blog gets the most out of it's multilingual audience. Check them out!

Other Websites Built with Polyglot

Feel free to open a PR and list your multilingual blog here you may want to share:


Currently supports Jekyll 3.0 , and Jekyll 4.0

  • Windows users will need to disable parallel_localization on their machines by setting parallel_localization: false in the _config.yml
  • In Jekyll 4.0 , SCSS source maps will generate improperly due to how Polyglot operates. The workaround is to disable the CSS sourcemaps. Adding the following to your config.yml will disable sourcemap generation:
    sourcemap: never


Please! I need all the support I can get! 🙏

But for real I would appreciate any contributions and support. This started as an open-source side-project and has gotten bigger than I'd ever imagine! If you have something you'd like to contribute to jekyll-polyglot, please open a PR!

2.0 Roadmap

    • site language: portuguese pt_BR pt_PT
    • site language: arabic ar
    • site language: japanese ja
    • site language: russian ru
    • site language: dutch nl
    • site language: korean ko
    • site language: hebrew he
    • get whitelisted as an official github-pages jekyll plugin
    • update CI provider

Download Details:

Author: untra
Source Code: 
License: MIT license

#jekyll #i18n #seo 

Polyglot: Multilingual and I18n Support tool for Jekyll Blogs

The Translation Component Provides tools To internationalize Your App

Translation Component

The Translation component provides tools to internationalize your application.

Getting Started

$ composer require symfony/translation
use Symfony\Component\Translation\Translator;
use Symfony\Component\Translation\Loader\ArrayLoader;

$translator = new Translator('fr_FR');
$translator->addLoader('array', new ArrayLoader());
$translator->addResource('array', [
    'Hello World!' => 'Bonjour !',
], 'fr_FR');

echo $translator->trans('Hello World!'); // outputs « Bonjour ! »


The Translation component for Symfony 6.1 is backed by:

  • Crowdin, a cloud-based localization management software helping teams to go global and stay agile.

Help Symfony by sponsoring its development!


Download Details:

Author: Symfony
Source Code: 
License: MIT license

#php #i18n #translation #symfony 

The Translation Component Provides tools To internationalize Your App

Gettext.jl: i18n for Julia


An interface to the gettext internationalization/translation interface.


Within Julia, run Pkg.update() and then Pkg.add("Gettext")


A simple string can be translated as follows:

using Gettext

bindtextdomain("sample", "po/")

println(_"Hello, world!")

In fact, such a sample program can be run from the toplevel directory of this repository as follows:

$ LANGUAGE=fr julia helloworld.jl
Bonjour le monde !

String interpolation

For string interpolation, you will need to use a runtime method (e.g. Formatting.jl) rather than Julia's built-in compile-time interpolation syntax. If using Formatting.jl, it probably makes sense to use the "Python" formatting style, as it allows the translations to have different argument orders than the original strings. For example,

using Gettext
using Formatting

bindtextdomain("sample", "po/")

daystr(n) = format(ngettext("{1} day", "{1} days", n), n)


When run, this gives

$ LANGUAGE=fr julia daystr.jl
1 jour
3 jours


Currently this library relies on Python's built-in implementation via PyCall. In the future, it may make sense to port this code into a Julia-native version (see issue 1).

Download Details:

Author: Julia-i18n
Source Code: 
License: View license

#julia #i18n 

Gettext.jl: i18n for Julia
Dexter  Goodwin

Dexter Goodwin


5 Favorite JavaScript I18n And L10n Libraries

In today's post we will learn about 5 Favorite JavaScript I18n And L10n Libraries.

Internationalization (i18n) is the process of developing products in such a way that they can be localized for languages and cultures easily. Localization (l10n), is the process of adapting applications and text to enable their usability in a particular cultural or linguistic market. For application developers, internationalizing an application means abstracting all of the strings and other locale-specific bits (such as date or currency formats) out of the application. Localizing an application means providing translations and localized formats for the abstracted bits.

Table of contents:

  • i18next - Internationalisation (i18n) with JavaScript the easy way.
  • Polyglot - Tiny i18n helper library.
  • Babelfish - i18n with human friendly API and built in plurals support.
  • TTag - Modern javascript i18n localization library based on ES6 tagged templates and the good old GNU gettext.
  • Attranslate - A JavaScript-tool for synchronizing translation-files, including JSON/YAML/XML and other formats.

1 - i18next: Internationalisation (i18n) with JavaScript the easy way.

i18next is a very popular internationalization framework for browser or any other javascript environment (eg. Node.js, Deno).

i18next provides:

For more information visit the website:

Our focus is providing the core to building a booming ecosystem. Independent of the building blocks you choose, be it react, angular or even good old jquery proper translation capabilities are just one step away.

View on Github

2 - Polyglot: Tiny i18n helper library.

Polyglot.js is a tiny I18n helper library written in JavaScript, made to work both in the browser and in CommonJS environments (Node). It provides a simple solution for interpolation and pluralization, based off of Airbnb’s experience adding I18n functionality to its Backbone.js and Node apps.


install with npm:

$ npm install node-polyglot

Running the tests

Clone the repo, run npm install, and npm test.



First, create an instance of the Polyglot class, which you will use for translation.

var polyglot = new Polyglot();

Polyglot is class-based so you can maintain different sets of phrases at the same time, possibly in different locales. This is very useful for example when serving requests with Express, because each request may have a different locale, and you don’t want concurrent requests to clobber each other’s phrases.

See Options Overview for information about the options object you can choose to pass to new Polyglot.


Tell Polyglot what to say by simply giving it a phrases object, where the key is the canonical name of the phrase and the value is the already-translated string.

  "hello": "Hello"

=> "Hello"

You can also pass a mapping at instantiation, using the key phrases:

var polyglot = new Polyglot({phrases: {"hello": "Hello"}});

Polyglot doesn’t do the translation for you. It’s up to you to give it the proper phrases for the user’s locale.

View on Github

3 - Babelfish: i18n with human friendly API and built in plurals support.

Internationalization with easy syntax for node.js and browser.

Classic solutions use multiple phrases for plurals. Babelfish defines plurals inline instead - that's more compact, and easy for programmers. Also, phrases are grouped into nested scopes, like in Ruby.

BabelFish supports all plural rules from unicode CLDR (via plurals-cldr).



$ npm install babelfish


$ bower install babelfish

Use es5-shim for old browsers compatibility.

Phrases Syntax

  • #{varname} Echoes value of variable
  • ((Singular|Plural1|Plural2)):count Plural form


  • А у меня в кармане #{nails_count} ((гвоздь|гвоздя|гвоздей)):nails_count

You can also omit anchor variable for plurals, by default it will be count. Thus following variants are equal:

  • I have #{count} ((nail|nails))
  • I have #{count} ((nail|nails)):count

Also you can use variables in plural parts:

  • I have ((#{count} nail|#{count} nails))

Need special zero form or overwrite any specific value? No problems:

  • I have ((=0 no nails|#{count} nail|#{count} nails))

View on Github

4 - TTag: Modern javascript i18n localization library based on ES6 tagged templates and the good old GNU gettext.

Modern javascript i18n localization library based on ES6 tagged templates and the good old GNU gettext.

Usage example

import { t, ngettext, msgid } from 'ttag'

// formatted strings
const name = 'Mike';
const helloMike = t`Hello ${name}`;

// plurals (works for en locale out of the box)
const n = 5;
const msg = ngettext(msgid`${ n } task left`, `${ n } tasks left`, n)


npm install --save ttag


You may also need to install ttag-cli for po files manipulation.

ttag cli -

npm install --save-dev ttag-cli

Usage from CDN

This project is designed to work in pair with babel-plugin-ttag.
But you can also play with it without transpilation.

View on Github

5 - Attranslate: A JavaScript-tool for synchronizing translation-files, including JSON/YAML/XML and other formats.

attranslate is a tool for synchronizing translation-files, including JSON/YAML/XML and other formats. attranslate is optimized for smooth rollouts in hectic project environments, even if you already have many translations. Optionally, attranslate works with automated translation-services. For example, let's say that a translation-service achieves 80% correct translations. With attranslate, a quick fix of the remaining 20% may be faster than doing everything by hand. Other than that, attranslate supports purely manual translations or even file-format-conversions without changing the language.

Why attranslate?

In contrast to paid services, a single developer can integrate attranslate in a matter of minutes. attranslate can operate on the very same translations-files that you are already using. This is possible because attranslate operates on your file in a surgical way, with as little changes as possible.


Cross-platform Support

attranslate is designed to translate any website or app with any toolchain. attranslate works for i18n/JavaScript-frameworks/Android/iOS/Flutter/Ruby/Jekyll/Symfony/Django/WordPress and many other platforms. To make this possible, attranslate supports the following file formats:

  • Flat or nested JSON
  • Flat or nested YAML
  • PO/POT-files
  • Android-XML or any other XMLs with text-contents
  • iOS-Strings
  • Flutter-ARB
  • CSV (e.g. for Google Docs or Microsoft Excel)

Preserve Manual Translations

attranslate recognizes that automated translations are not perfect. Therefore, whenever you are unhappy with the produced results, attranslate allows you to simply overwrite texts in your target-files. attranslate will never ever overwrite a manual correction in subsequent runs.

Optionally Overwrite Outdated Translations

attranslate is capable of detecting outdated translations. Overwriting outdated translations helps to ensure the freshness of translations. However, in hectic project environments, it might be easier to leave outdated translations as-is. Therefore, attranslate leaves outdated translations as-is unless you explicitly configure it to overwrite them.

Available Services

attranslate supports the following translation-services:

  • manual: Translate texts manually by entering them into attranslate.
  • Google Cloud Translate
  • Azure Translator
  • sync-without-translate: Does not change the language. This can be useful for converting between file formats, or for maintaining region-specific differences.

View on Github

Thank you for following this article.

Related videos:

i18next Crash Course | the JavaScript i18n framework

#javascript #i18n #localization 

5 Favorite JavaScript I18n And L10n Libraries
Gordon  Taylor

Gordon Taylor


i18n-extract: Manage Localization with Static analysis


Manage localization with static analysis. 


npm install --save-dev i18n-extract

The problem solved

This module analyses code statically for key usages, such as i18n.t('some.key'), in order to:

  • Report keys that are missing
  • Report keys that are unused.
  • Report keys that are highly duplicated.

E.g. This module works well in conjunction with:

Supported keys

  • static:
  • string concatenation:
i18n('key.' + 'concat')
  • template string:
  • dynamic:
  • comment:
/* i18n-extract key.comment */


extractFromCode(code, [options])

Parse the code to extract the argument of calls of i18n(key).

  • code should be a string.
  • Return an array containing keys used.


import {extractFromCode} from 'i18n-extract';
const keys = extractFromCode("const followMe = i18n('b2b.follow');", {
  marker: 'i18n',
// keys = ['b2b.follow']

extractFromFiles(files, [options])

Parse the files to extract the argument of calls of i18n(key).

  • files can be either an array of strings or a string. You can also use a glob.
  • Return an array containing keys used in the source code.


import {extractFromFiles} from 'i18n-extract';
const keys = extractFromFiles([
], {
  marker: 'i18n',


  • marker: The name of the internationalized string marker function. Defaults to i18n.
  • keyLoc: An integer indicating the position of the key in the arguments. Defaults to 0. Negative numbers, e.g., -1, indicate a position relative to the end of the argument list.
  • parser: Enum indicate the parser to use, can be typescript or flow. Defaults to flow.
  • babelOptions: A Babel configuration object to allow applying custom transformations or plugins before scanning for i18n keys. Defaults to a config with all babylon plugins enabled.

findMissing(locale, keysUsed)

Report the missing keys. Those keys should probably be translated.

  • locale should be a object containing the translations.
  • keysUsed should be an array. Containes the keys used in the source code. It can be retrieve with extractFromFiles our extractFromCode.
  • Return a report.


import {findMissing} from 'i18n-extract';
const missing = findMissing({
  key1: 'key 1',
}, ['key1', 'key2']);

 * missing = [{
 *   type: 'MISSING',
 *   key: 'key2',
 * }];


findUnused(locale, keysUsed)

Report the unused key. Those keys should probably be removed.

  • locale should be a object containing the translations.
  • keysUsed should be an array. Containes the keys used in the source code. It can be retrieve with extractFromFiles our extractFromCode.
  • Return a report.


import {findUnused} from 'i18n-extract';
const unused = findUnused({
  key1: 'key 1',
  key2: 'key 2',
}, ['key1']);

 * unused = [{
 *   type: 'UNUSED',
 *   key: 'key2',
 * }];

findDuplicated(locale, keysUsed, options)

Report the duplicated key. Those keys should probably be mutualized. The default threshold is 1, it will report any duplicated translations.

  • locale should be a object containing the translations.
  • keysUsed should be an array. Containes the keys used in the source code. It can be retrieve with extractFromFiles our extractFromCode.
  • options should be an object. You can provide a threshold property to change the number of duplicated value before it's added to the report.
  • Return a report.


import {findDuplicated} from 'i18n-extract';
const duplicated = findDuplicated({
  key1: 'Key 1',
  key2: 'Key 2',
  key3: 'Key 2',

 * unused = [{
 *   type: 'DUPLICATED',
 *   keys: [
 *     'key2',
 *     'key3',
 *   ],
 *   value: 'Key 2',
 * }];

forbidDynamic(locale, keysUsed)

Report any dynamic key. It's arguably more dangerous to use dynamic key. They may break.

  • locale should be a object containing the translations.
  • keysUsed should be an array. Containes the keys used in the source code. It can be retrieve with extractFromFiles our extractFromCode.
  • Return a report.


import {forbidDynamic} from 'i18n-extract';
const forbidDynamic = forbidDynamic({}, ['key.*']);

 * forbidDynamic = [{
 *   type: 'FORBID_DYNAMIC',
 *   key: 'key.*',
 * }];


Flatten the object.

  • object should be a object.


import {flatten} from 'i18n-extract';
const flattened = flatten({
  key2: 'Key 2',
  key4: {
    key41: 'Key 4.1',
    key42: {
      key421: 'Key 4.2.1',

 * flattened = {
 *   key2: 'Key 2',
 *   'key4.key41': 'Key 4.1',
 *   'key4.key42.key421': 'Key 4.2.1',
 * };

mergeMessagesWithPO(messages, poInput, poOutput)

Output a new po file with only the messages present in messages. If a message is already present in the poInput, we keep the translation. If a message is not present, we add a new empty translation.

  • messages should be an array.
  • poInput should be a string.
  • poOutput should be a string.


import {mergeMessagesWithPO} from 'i18n-extract';

const messages = ['Message 1', 'Message 2'];
mergeMessagesWithPO(messages, 'messages.po', 'messages.output.po');

 * Will output :
 * > messages.output.po has 812 messages.
 * > We have added 7 messages.
 * > We have removed 3 messages.

Download Details:

Author: oliviertassinari
Source Code: 
License: MIT license

#javascript #i18n #extract 

i18n-extract: Manage Localization with Static analysis
Lawson  Wehner

Lawson Wehner


Dynamic_i18n: Flutter Dynamically Load Translation in Your App

Dynamically load translation in your app.

Getting started

Initiate I18n in your dart's main() {}

void main() {
    // ...

        url: '',
        locale: 'hi',
        locales: ['en', 'hi', 'pa'],

    // ... 

Now you can use I18n.builder anywhere in your code

    children: [
        // example 1: for Text
        I18n.text('Who am I ?'), // Text('मैं कौन हूँ ?')

        // example 2: with builder
        // first build with given locale
        // rebuild after translation fetching
        I18n.builder('Who am I ?', (translatedText) {
            return Text(translatedText);

        // example 3: with childBuilder
            'Who am I ?',
            (translatedText, child) {
                return Column(
                children: [
                    child, // same on both first and second build
            Container(), // will be reused in rebuild

Server Setup

Set your server to respond in this way.

Request Method: GET
Request URL:

Content-Type: application/json

    {'en': 'How are you ?', 'hi': 'आप कैसे हो ?'},
    {'en': 'Who am I ?', 'hi': 'मैं कौन हूँ ?'},

For single translation request library will send POST request in this format to url you have provided in I18n.init, according to above example it will be

Request Method: POST
Request URL:

    'en': sourceText,
    'target': targetLocale,

Content-Type: application/json

{'en': 'How are you ?', 'hi': 'आप कैसे हो ?'}

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add dynamic_i18n

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

  dynamic_i18n: ^0.0.6

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:dynamic_i18n/dynamic_i18n.dart';


import 'package:flutter/material.dart';
import 'package:dynamic_i18n/dynamic_i18n.dart';

// Stateful widget example 1
// if translation exists then it will be returned otherwise
// setState will be called after translation fetch
class SomeStatefulWidget extends StatefulWidget {
  const SomeStatefulWidget({Key? key}) : super(key: key);

  _SomeStatefulWidgetState createState() => _SomeStatefulWidgetState();

class _SomeStatefulWidgetState extends State<SomeStatefulWidget> {
  late I18n locale;

  initState() {
    locale = I18n(this);


  build(BuildContext context) {
    return Text(locale.get('Hello world'));

// Stateful widget example 2
// by extending I18nState you can remove boilerplate
class AnotherStatefulWidget extends StatefulWidget {
  const AnotherStatefulWidget({Key? key}) : super(key: key);

  _AnotherStatefulWidgetState createState() => _AnotherStatefulWidgetState();

class _AnotherStatefulWidgetState extends I18nState<AnotherStatefulWidget> {
  build(BuildContext context) {
    return Text(i18n('Hello world'));

// Stateless widget example
class SomeStatelessWidget extends StatelessWidget {
  const SomeStatelessWidget({Key? key}) : super(key: key);

  Widget build(BuildContext context) {
    return Column(
      children: [
        // example 1: for Text
        I18n.text('Who am I ?'), // Text('मैं कौन हूँ ?')

        // example 2: with builder
        // first build with given locale
        // rebuild after translation fetching
        I18n.builder('Who am I ?', (translatedText) {
          return Text(translatedText);

        // example 3: with childBuilder
          'Who am I ?',
          (translatedText, child) {
            return Column(
              children: [
                child, // same on both first and second build
          Container(), // will be reused in rebuild

Download Details:

Author: hsbijarniya
Source Code: 
License: MIT license

#flutter #dart #dynamic #i18n 

Dynamic_i18n: Flutter Dynamically Load Translation in Your App
Rupert  Beatty

Rupert Beatty


Linguist: Multilingual Urls and Redirects for Laravel

Linguist - Multilingual urls and redirects for Laravel

This package provides an easy multilingual urls and redirection support for the Laravel framework.

In short Laravel will generate localized urls for links and redirections.


Linguist works perfectly well with named Laravel routes for javascript package!


Linguist is very easy to use. The locale slug is removed from the REQUEST_URI leaving the developer with the cleanest multilingual environment possible.

Install using Composer:

composer require keevitaja/linguist

There are several options to make Linguist work.

Option 1: Modify the public/index.php

Add following line after the vendor autoloading to your projects public/index.php file.

(new Keevitaja\Linguist\UriFixer)->fixit();

End result would be this:

| Register The Auto Loader
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels great to relax.

require __DIR__.'/../vendor/autoload.php';

(new Keevitaja\Linguist\UriFixer)->fixit();

Option 2: Use LocalizedKernel

Note: This option works only if you have not changed your applications root namespace. Default is App.

In your projects bootstrap/app.php swap the App\Http\Kernel with Keevitaja\Linguist\LocalazedKernel:

| Bind Important Interfaces
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.


Option 3: modify the App\Http\Kernel

Note: This also works with custom root namespace.


namespace App\Http;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Routing\Router;
use Keevitaja\Linguist\UriFixer;

class Kernel extends HttpKernel

    public function __construct(Application $app, Router $router)
        (new UriFixer)->fixit();

        parent::__construct($app, $router);

Publish config

Finally you need to publish the Linguist config to set your enabled locales and other relavant configurations.

php artisan vendor:publish --provider="Keevitaja\Linguist\LinguistServiceProvider"

Your personal configuration file will be config/linguist.php.


You can add the LocalizeUrls middleware your web middleware group as the first item to get the linguist support:

 * The application's route middleware groups.
 * @var array
protected $middlewareGroups = [
    'web' => [

Note: This middleware has to be the first item in group!

Another option is to use Linguist in your applications service provider:

class AppServiceProvider extends ServiceProvider
     * Bootstrap any application services.
     * @return void
    public function boot(\Keevitaja\Linguist\Linguist $linguist)

UrlGenerator will add the locale slug in front of the URI when needed. No extra actions needed.

Route::get('people', ['as' => 'people.index', 'uses' => ''PeopleController@index'']);
{{ route('people.index') }} or {{ url('people') }} // default locale from linguist config

Switcher is a little helper to get the current URLs for the locale switcher.

$urls = dispatch_now(new \Keevitaja\Linguist\Switcher);

NB! Both config and route caching are working!


Use linguist helpers for a correct routing of assets

Regular Assets

<link rel="stylesheet" href="{{ linguist_asset('css/style.css') }}">
<script type="text/javascript" src="{{ linguist_asset('js/my_js.js') }}"></script>

Secure Assets

<link rel="stylesheet" href="{{ secure_linguist_asset('css/style.css') }}">
<script type="text/javascript" src="{{ secure_linguist_asset('js/my_js.js') }}"></script>


To make localization work in queues you need to run Linguist->localize($theLocaleYouWant) inside the queued item.

Author: Keevitaja
Source Code: 
License: MIT license

#laravel #i18n #php #translation #localization 

Linguist: Multilingual Urls and Redirects for Laravel
Rupert  Beatty

Rupert Beatty


Date: A Library to Help You Work with Dates in Multiple Languages


This date library extends Carbon with multi-language support. Methods such as format, diffForHumans, parse, createFromFormat and the new timespan, will now be translated based on your locale.

All translations made by contributors have been moved to the Carbon 2 package. This package now uses the Carbon translations to provide you with better multi-language support. Translation issues should be reported on the Carbon repository. Please also check out the original documentation here.


Install using composer:

composer require jenssegers/date


There is a service provider included for integration with the Laravel framework. This provider will get the application locale setting and use this for translations. This service will be automatically registered if you use Laravel 5.5+ using the auto-discovery. Else to register the service provider, add the following to the providers array in config/app.php:


You can also add it as a Facade in config/app.php:

'Date' => Jenssegers\Date\Date::class,


This package contains language files for the following languages (

  • Afar (aa)
  • Afrikaans (af)
  • Aghem (agq)
  • Aguaruna (agr)
  • Akan (ak)
  • Amharic (am)
  • Aragonese (an)
  • Angika (anp)
  • Arabic (ar)
  • Assamese (as)
  • Asu (asa)
  • Asturian (ast)
  • Southern Aymara (ayc)
  • Azerbaijani (az)
  • Basaa (bas)
  • Belarusian (be)
  • Bemba (bem)
  • ber (ber)
  • Bena (bez)
  • Bulgarian (bg)
  • Bhili (bhb)
  • Bhojpuri (bho)
  • Bislama (bi)
  • Bambara (bm)
  • Bengali (bn)
  • Tibetan (bo)
  • Breton (br)
  • Bodo (brx)
  • Bosnian (bs)
  • Bilin (byn)
  • Catalan (ca)
  • Chakma (ccp)
  • Chechen (ce)
  • Chiga (cgg)
  • Cherokee (chr)
  • Chinese (cmn)
  • Crimean Turkish (crh)
  • Czech (cs)
  • Kashubian (csb)
  • Church Slavic (cu)
  • Chuvash (cv)
  • Welsh (cy)
  • Danish (da)
  • Taita (dav)
  • German (de)
  • Zarma (dje)
  • Dogri (macrolanguage) (doi)
  • Lower Sorbian (dsb)
  • Duala (dua)
  • Divehi (dv)
  • Jola-Fonyi (dyo)
  • Dzongkha (dz)
  • Embu (ebu)
  • Ewe (ee)
  • Greek (modern) (el)
  • English (en)
  • Esperanto (eo)
  • Spanish (es)
  • Estonian (et)
  • Basque (eu)
  • Ewondo (ewo)
  • Persian (fa)
  • Fulah (ff)
  • Finnish (fi)
  • Filipino (fil)
  • Faroese (fo)
  • French (fr)
  • Friulian (fur)
  • Western Frisian (fy)
  • Irish (ga)
  • Gaelic (gd)
  • Geez (gez)
  • Galician (gl)
  • Konkani (gom)
  • Swiss German (gsw)
  • Gujarati (gu)
  • Gusii (guz)
  • Manx (gv)
  • Hausa (ha)
  • Hakka Chinese (hak)
  • Hawaiian (haw)
  • Hebrew (modern) (he)
  • Hindi (hi)
  • Fiji Hindi (hif)
  • Chhattisgarhi (hne)
  • Croatian (hr)
  • Upper Sorbian (hsb)
  • Haitian (ht)
  • Hungarian (hu)
  • Armenian (hy)
  • i18n (i18n)
  • Interlingua (ia)
  • Indonesian (id)
  • Igbo (ig)
  • Sichuan Yi (ii)
  • Inupiaq (ik)
  • in (in)
  • Icelandic (is)
  • Italian (it)
  • Inuktitut (iu)
  • iw (iw)
  • Japanese (ja)
  • Ngomba (jgo)
  • Machame (jmc)
  • Javanese (jv)
  • Georgian (ka)
  • Kabyle (kab)
  • Kamba (kam)
  • Makonde (kde)
  • Kabuverdianu (kea)
  • Koyra Chiini (khq)
  • Kikuyu (ki)
  • Kazakh (kk)
  • Kako (kkj)
  • Kalaallisut (kl)
  • Kalenjin (kln)
  • Central Khmer (km)
  • Kannada (kn)
  • Korean (ko)
  • Konkani (kok)
  • Kashmiri (ks)
  • Shambala (ksb)
  • Bafia (ksf)
  • Colognian (ksh)
  • Kurdish (ku)
  • Cornish (kw)
  • Kirghiz (ky)
  • Langi (lag)
  • Luxembourgish (lb)
  • Ganda (lg)
  • Limburgan (li)
  • Ligurian (lij)
  • Lakota (lkt)
  • Lingala (ln)
  • Lao (lo)
  • Northern Luri (lrc)
  • Lithuanian (lt)
  • Luba-Katanga (lu)
  • Luo (luo)
  • Luyia (luy)
  • Latvian (lv)
  • Literary Chinese (lzh)
  • Magahi (mag)
  • Maithili (mai)
  • Masai (mas)
  • Meru (mer)
  • Morisyen (mfe)
  • Malagasy (mg)
  • Makhuwa-Meetto (mgh)
  • Metaʼ (mgo)
  • Eastern Mari (mhr)
  • Maori (mi)
  • Mískito (miq)
  • Karbi (mjw)
  • Macedonian (mk)
  • Malayalam (ml)
  • Mongolian (mn)
  • Manipuri (mni)
  • mo (mo)
  • Marathi (mr)
  • Malay (ms)
  • Maltese (mt)
  • Mundang (mua)
  • Burmese (my)
  • Mazanderani (mzn)
  • Min Nan Chinese (nan)
  • Nama (naq)
  • Norwegian Bokmål (nb)
  • North Ndebele (nd)
  • Low German (nds)
  • Nepali (ne)
  • Central Nahuatl (nhn)
  • Niuean (niu)
  • Dutch (nl)
  • Kwasio (nmg)
  • Norwegian Nynorsk (nn)
  • Ngiemboon (nnh)
  • Norwegian (no)
  • South Ndebele (nr)
  • Northern Sotho (nso)
  • Nuer (nus)
  • Nyankole (nyn)
  • Occitan (oc)
  • Oromo (om)
  • Oriya (or)
  • Ossetian (os)
  • Panjabi (pa)
  • Papiamento (pap)
  • Polish (pl)
  • Prussian (prg)
  • Pashto (ps)
  • Portuguese (pt)
  • Quechua (qu)
  • Cusco Quechua (quz)
  • Rajasthani (raj)
  • Romansh (rm)
  • Rundi (rn)
  • Romanian (ro)
  • Rombo (rof)
  • Russian (ru)
  • Kinyarwanda (rw)
  • Rwa (rwk)
  • Sanskrit (sa)
  • Sakha (sah)
  • Samburu (saq)
  • Santali (sat)
  • Sangu (sbp)
  • Sardinian (sc)
  • Sindhi (sd)
  • Northern Sami (se)
  • Sena (seh)
  • Koyraboro Senni (ses)
  • Sango (sg)
  • Samogitian (sgs)
  • sh (sh)
  • Tachelhit (shi)
  • Shan (shn)
  • Shuswap (shs)
  • Sinhala (si)
  • Sidamo (sid)
  • Slovak (sk)
  • Slovene (sl)
  • Samoan (sm)
  • Inari Sami (smn)
  • Shona (sn)
  • Somali (so)
  • Albanian (sq)
  • Serbian (sr)
  • Swati (ss)
  • Southern Sotho (st)
  • Swedish (sv)
  • Swahili (sw)
  • Silesian (szl)
  • Tamil (ta)
  • Tulu (tcy)
  • Telugu (te)
  • Teso (teo)
  • Tetum (tet)
  • Tajik (tg)
  • Thai (th)
  • Chitwania Tharu (the)
  • Tigrinya (ti)
  • Tigre (tig)
  • Turkmen (tk)
  • Tagalog (tl)
  • Klingon (tlh)
  • Tswana (tn)
  • Tongan (Tonga Islands) (to)
  • Tok Pisin (tpi)
  • Turkish (tr)
  • Tsonga (ts)
  • Tatar (tt)
  • Tasawaq (twq)
  • Talossan (tzl)
  • Tamazight (tzm)
  • Uighur (ug)
  • Ukrainian (uk)
  • Unami (unm)
  • Urdu (ur)
  • Uzbek (uz)
  • Vai (vai)
  • Venda (ve)
  • Vietnamese (vi)
  • Volapük (vo)
  • Vunjo (vun)
  • Walloon (wa)
  • Walser (wae)
  • Wolaytta (wal)
  • Wolof (wo)
  • Xhosa (xh)
  • Soga (xog)
  • Yangben (yav)
  • Yiddish (yi)
  • Yoruba (yo)
  • Cantonese (yue)
  • Yau (Morobe Province) (yuw)
  • Standard Moroccan Tamazight (zgh)
  • Chinese (zh)
  • Zulu (zu)


The Date class extends the Carbon methods such as format and diffForHumans, and translates them based on your locale:

use Jenssegers\Date\Date;


echo Date::now()->format('l j F Y H:i:s'); // zondag 28 april 2013 21:58:16

echo Date::parse('-1 day')->diffForHumans(); // 1 dag geleden

The Date class also added some aliases and additional methods such as: ago which is an alias for diffForHumans, and the timespan method:

echo $date->timespan(); // 3 months, 1 week, 1 day, 3 hours, 20 minutes

Methods such as parse and createFromFormat also support "reverse translations". When calling these methods with translated input, it will try to translate it to English before passing it to DateTime:

$date = Date::createFromFormat('l d F Y', 'zaterdag 21 maart 2015');


Carbon is the library the Date class is based on. All of the original Carbon operations are still available, check out for more information. Here are some of the available methods:

Creating dates

You can create Date objects just like the DateTime object (

$date = new Date();
$date = new Date('2000-01-31');
$date = new Date('2000-01-31 12:00:00');

// With time zone
$date = new Date('2000-01-31', new DateTimeZone('Europe/Brussels'));

You can skip the creation of a DateTimeZone object:

$date = new Date('2000-01-31', 'Europe/Brussels');

Create Date objects from a relative format (

$date = new Date('now');
$date = new Date('today');
$date = new Date('+1 hour');
$date = new Date('next monday');

This is also available using these static methods:

$date = Date::parse('now');
$date = Date::now();

Creating a Date from a timestamp:

$date = new Date(1367186296);

Or from an existing date or time:

$date = Date::createFromDate(2000, 1, 31);
$date = Date::createFromTime(12, 0, 0);
$date = Date::create(2000, 1, 31, 12, 0, 0);

Formatting Dates

You can format a Date object like the DateTime object (

echo Date::now()->format('Y-m-d'); // 2000-01-31

The Date object can be cast to a string:

echo Date::now(); // 2000-01-31 12:00:00

Get a human readable output (alias for diffForHumans):

echo $date->ago(); // 5 days ago

Calculate a timespan:

$date = new Date('+1000 days');
echo Date::now()->timespan($date);
// 2 years, 8 months, 3 weeks, 5 days

// or even
echo Date::now()->timespan('+1000 days');

Get years since date:

$date = new Date('-10 years');
echo $date->age; // 10

$date = new Date('+10 years');
echo $date->age; // -10

Manipulating Dates

You can manipulate by using the add and sub methods, with relative intervals (

$yesterday = Date::now()->sub('1 day');
$tomorrow  = Date::now()->add('1 day');

// ISO 8601
$date = Date::now()->add('P2Y4DT6H8M');

You can access and modify all date attributes as an object:

$date->year = 2013:
$date->month = 1;
$date->day = 31;

$date->hour = 12;
$date->minute = 0;
$date->second = 0;


Language contributions should made to

Security contact information

To report a security vulnerability, follow these steps.

Author: jenssegers
Source Code: 
License: MIT license

#laravel #i18n #carbon 

Date: A Library to Help You Work with Dates in Multiple Languages
Rupert  Beatty

Rupert Beatty


Laravel-translatable: Making Eloquent Models Translatable

A trait to make Eloquent models translatable

This package contains a trait to make Eloquent models translatable. Translations are stored as json. There is no extra table needed to hold them.

Once the trait is installed on the model you can do these things:

$newsItem = new NewsItem; // This is an Eloquent model
   ->setTranslation('name', 'en', 'Name in English')
   ->setTranslation('name', 'nl', 'Naam in het Nederlands')

$newsItem->name; // Returns 'Name in English' given that the current app locale is 'en'
$newsItem->getTranslation('name', 'nl'); // returns 'Naam in het Nederlands'


$newsItem->name; // Returns 'Naam in het Nederlands'


composer test


Please see CONTRIBUTING for details.


If you've found a bug regarding security please mail instead of using the issue tracker.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.


All documentation is available on our documentation site.


You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.

We publish all received postcards on our company website.


We got the idea to store translations as json in a column from Mohamed Said. Parts of the readme of his multilingual package were used in this readme.

Author: Spatie
Source Code: 
License: MIT license

#laravel #i18n #php #eloquent 

Laravel-translatable: Making Eloquent Models Translatable
Royce  Reinger

Royce Reinger


Numbers_and_words: Convert Numbers to Words using I18N


Spell out numbers in several languages using the I18n gem.

Перевод чисел в слова при помощи библиотеки I18n.

Converti les nombres en lettres en utilisant la librairie I18n.

Számok betűvel írva az I18n könyvtár segítségével.

I18n kütüphanesi ile sayıları yazıya çevirir.

Soletra números em vários idiomas utilizando a biblioteca I18n.

Deletrea números en varios idiomas utilizando la gema I18n.

Supported Languages / Языки / Langues Supportées

English [en]

Spanish [es]

Русский [ru]

Français [fr]

Українська [ua]

Magyar [hu]

Lietuvių [lt]

Latviešu [lv]

Eesti [et]

ქართული (Georgian) [ka]

Türkçe [tr]

Deutsch** [de]

Italiano** [it]

Nederlands [nl]

Swedish** [se]

English (British)** [en-GB]

Česky [cs]

Português [pt]

Português Brasileiro [pt-BR]

հայերեն (Armenian) [hy]

Қазақша [kz]

** Experimental

{[en-AU], [en-CA], [en-IN], [en-US]}*** => [en]

{[fr-CA], [fr-CH]}*** => [fr]

{[de], [de-AT], [de-CH]}*** => [de]

{[it-CH]}*** => [it]

*** Aliases

Examples / Примеры / Exemples

I18n.with_locale(:en) { 42.to_words }
=> "forty-two"

I18n.with_locale(:es) { 42.to_words }
=> "cuarenta y dos"

I18n.with_locale(:ru) { 42.to_words }
=> "сорок два"

I18n.with_locale(:fr) { 42.to_words }
=> "quarante-deux"

I18n.with_locale(:hu) { 42.to_words }
=> "negyvenkettő"

I18n.with_locale(:lt) { 42.to_words }
=> "keturiasdešimt du"

I18n.with_locale(:lv) { 42.to_words }
=> "četrdesmit divi"

I18n.with_locale(:et) { 42.to_words }
=> "nelikümmend kaks"

I18n.with_locale(:ka) { 42.to_words }
=> "ორმოცდაორი"

I18n.with_locale(:cs) { 42.to_words }
=> "čtyřicetdva"

I18n.with_locale(:vi) { 42.to_words }
=> "bốn mươi hai"

I18n.with_locale(:hy) { 42.to_words }
=> "քառասուն երկու"

I18n.with_locale(:kz) { 42.to_words }
=> "қырық екi"

=> "twenty-one"
=> "veintiuno"
=> "двадцать один"
=> "vingt et un"
=> "двадцять один"
=> "huszonegy"
=> "dvidešimt vienas"
=> "divdesmit viens"
=> "kakskümmend üks"
=> "yirmi bir"
=> "ოცდაერთი"
=> "einundzwanzig"
=> "ventiuno"
=> "eenentwintig"
=> "tjugo-en"
=> "dvacetjedna"
=> "vinte e um"
=> "hai mươi mốt"
=> "քսան մեկ"
=> "жиырма бiр"

=> "two hundred thirty-one"
=> "doscientos treinta y uno"
=> "двести тридцать один"
=> "deux cent trente et un"
=> "двiстi тридцять один"
=> "kettőszázharmincegy"
=> "du šimtai trisdešimt vienas"
=> "divi simti trīsdesmit viens"
=> "kakssada kolmkümmend üks"
=> "ორას ოცდათერთმეტი"
=> "iki yüz otuz bir"
=> "zweihunderteinunddreißig"
=> "2 cento trentauno"
=> "tweehonderdeenendertig"
=> "två hundra trettio-en"
=> "dvě stě třicetjedna"
=> "duzentos e trinta e um"
=> "hai trăm ba mươi mốt"
=> "երկու հարյուր երեսուն մեկ"
=> "екi жүз отыз бiр"

=> "four thousand thirty"
=> "cuatro mil treinta"
=> "четыре тысячи тридцать"
=> "quatre mille trente"
=> "чотири тисячi тридцять"
=> "négyezer-harminc"
=> "keturi tūkstančiai trisdešimt"
=> "četri tūkstoši trīsdesmit"
=> "neli tuhat kolmkümmend"
=> "ოთხი ათას ოცდაათი"
=> "dört bin otuz"
=> "viertausenddreißig"
=> "quattro mille trenta"
=> "vierthousanddertig"
=> "fyra tusen trettio"
=> "čtyři tisíce třicet"
=> "bốn nghìn không trăm ba mươi"
=> "չորս հազար երեսուն"
=> "төрт мың отыз"

=> "one million one hundred"
=> "un millón cien"
=> "один миллион сто"
=> "un million cent"
=> "один мiльйон сто"
=> "egymillió-egyszáz"
=> "milijonas šimtas"
=> "viens miljons simts"
=> "üks miljon ükssada"
=> "ერთი მილიონ ასი"
=> "bir milyon yüz"
=> "eine Million einhundert"
=> "uno milione 1 cento"
=> "één miljoen honderd"
=> "en miljoner en hundra"
=> "jeden milión jedno sto"
=> "một triệu một trăm"
=> "մեկ միլիոն հարյուր"
=> "бiр миллион бiр жүз"

=> "one decillion"
=> "mil quintillones"
=> "один дециллион"
=> "un quintilliard"
=> "один децильйон"
=> "egykvintilliárd"
=> "vienas decilijonas"
=> "viens deciljons"
=> "üks dekiljon"
=> "bir desilyon"
=> "eine Quintilliarde"
=> "uno decillion"
=> "één decillion"
=> "en decillion"
=> "decilijon"
=> "бiр дециллион"

[1, 2, 3].to_words
=> ["one", "two", "three"]
=> ["uno", "dos", "tres"]
=> ["один", "два", "три"]
=> ["un", "deux", "trois"]
=> ["egy", "kettő", "három"]
=> ["vienas", "du", "trys"]
=> ["viens", "divi", "trīs"]
=> ["üks", "kaks", "kolm"]
=> ["ერთი", "ორი", "სამი"]
=> ["jedna", "dva", "tři"]
=> ["một", "hai", "ba"]
=> ["մեկ", "երկու", "երեք"]
=> ["бiр", "екi", "үш"]

[11, 22, 133].to_words
=> ["eleven", "twenty-two", "one hundred thirty-three"]
=> ["once", "veintidós", "ciento treinta y tres"]
=> ["одиннадцать", "двадцать два", "сто тридцать три"]
=> ["onze", "vingt-deux", "cent trente-trois"]
=> ["одинадцять", "двадцять два", "сто тридцять три"]
=> ["tizenegy", "huszonkettő", "egyszázharminchárom"]
=> ["vienuolika", "dvidešimt du", "šimtas trisdešimt trys"]
=> ["vienpadsmit", "divdesmit divi", "simtu trīsdesmit trīs"]
=> ["üksteist", "kakskümmend kaks", "ükssada kolmkümmend kolm"]
=> ["თერთმეტი", "ოცდაორი", "ას ოცდაცამეტი"]
=> ["on bir", "yirmi iki", "yüz otuz üç"]
=> ["elf", "zweiundzwanzig", "einhundertdreiunddreißig"]
=> ["undici", "ventidue", "1 cento trentatre"]
=> ["elf", "tweeëntwintig", "honderddrieëndertig"]
=> ["elva", "tjugo-två", "en hundra trettio-tre"]
=> ["jedenáct", "dvacetdva", "jedno sto třicettři"]
=> ["mười một", "hai mươi hai", "một trăm ba mươi ba"]
=> ["տասնմեկ", "քսան երկու", "հարյուր երեսուն երեք"]
=> ["он бiр", "жиырма екi", "бiр жүз отыз үш"]

=> "twenty-one and seventy-seven hundredths"
=> "двадцать одна целая и семьдесят семь сотых"
=> "двадцять одна цiла i сiмдесят сiм сотих"
=> "huszonegy egész hetvenhét század"
=> "twenty-one point seven seven"
=> "hai mươi mốt phẩy bảy mươi bảy"

Language options / Языковые опции

English / British

Ordinal form: (ordinal: [true || false])

I18n.with_locale(:en) { 21.to_words ordinal: true }
=> "twenty-first"

Remove hyphen between tens and ones: (remove_hyphen: [true || false])

I18n.with_locale(:en) { 21.to_words remove_hyphen: true }
=> "twenty one"

Add 'and' between hundreds and tens: (hundreds_with_union: [true || false])

I18n.with_locale(:en) { 111.to_words hundreds_with_union: true }
=> "one hundred and eleven"

Remove 'zero' from integral part of float: (remove_zero: [true || false])

I18n.with_locale(:en) { 0.7.to_words remove_zero: true }
=> "seven tenths"

I18n.with_locale(:en) { [0.1, 0.31, 0.12].to_words remove_zero: true }
=> ["one tenth", "thirty-one hundredths", "twelve hundredths"]


Change gender form: (gender: [:female || :male || :neuter])

I18n.with_locale(:ru) { 1001.to_words gender: :neuter }
=> "одна тысяча одно"


Change gender form: (gender: [:female || :male || :neuter])

I18n.with_locale(:ru) { 1001.to_words gender: :neuter }
=> "одна тисяча одне"


Ordinal form: (ordinal: [true || false])

I18n.with_locale(:hu) { 21.to_words ordinal: true }
=> "huszonegyedik"

Brazilian Portuguese

Change gender form: (gender: [:female || :male])

I18n.with_locale(:'pt-BR') { 1000000001.to_words gender: :female }
=> "um bilhão e uma"


Change gender form: (gender: [:female || :male || :neuter])

I18n.with_locale(:'cs') { 1.to_words gender: :female }
=> "jedna"

I18n.with_locale(:'cs') { 1.to_words gender: :male }
=> "jeden"

I18n.with_locale(:'cs') { 1.to_words gender: :neuter }
=> "jedno"

Remove 'zero' from integral part of float: (remove_zero: [true || false])

I18n.with_locale(:cs) { 0.7.to_words }
=> "nula celých sedm desetin"

I18n.with_locale(:cs) { 0.7.to_words remove_zero: true }
=> "sedm desetin"

Ordinal form: (ordinal: [true || false])

I18n.with_locale(:cs) { 21.to_words ordinal: true }
=> "dvacátý první"

I18n.with_locale(:cs) { 21.to_words gender: :female, ordinal: true }
=> "dvacátá první"


Change gender form: (gender: [:female || :male])

I18n.with_locale(:es) { 1.to_words gender: :female }
=> "una"

I18n.with_locale(:es) { 1.to_words gender: :male }
=> "uno"

Use the apocopated (shortened) form: (apocopated: [true || false])

I18n.with_locale(:es) { 1.to_words apocopated: true }
=> "un"

Mix gender and apocopated forms:

I18n.with_locale(:es) { 201.to_words gender: :female, apocopated: true }
=> "doscientas un"

Remove 'zero' from integral part of float: (remove_zero: [true || false])

I18n.with_locale(:es) { 0.7.to_words }
=> "cero con siete décimas"

I18n.with_locale(:es) { 0.7.to_words remove_zero: true }
=> "siete décimas"

Other options / Другие опции


You may pass argument :precision that added zeros to the end of float number:

I18n.with_locale(:ru) { 0.1.to_words precision: 3 }
=> ноль целых и десять сотых

I18n.with_locale(:es) { 0.2.to_words precision: 2 }
=> cero con veinte centésimas

Requirements / Требования / Configuration Requise

2.5 <= Ruby (compatible with/совместимость с/compatible avec Ruby 2.5 and/и/et JRuby);

0.5.0 <= I18n (earlier versions not tested/ранние версии не тестировались/versions précédentes non testées);

Installation / Установка / Installation

gem install numbers_and_words

Bugs and Language Support / Поправки и Новые Языки / Bugs et Support d'autres Langues

See for last changes.

Fork the project. Make your feature addition or bug fix with tests.

Send a pull request. Bonus points for topic branches.

Contacts / Контакты / Contacts

Kirill Lazarev (

Daniel Doubrovkine (

Sergey Shkirando (

Ulrich Sossou (Github, Personal Page)

eLod (

Mārtiņš Spriņģis (

Miks Miķelsons (

Author: kslazarev
Source Code: 
License: MIT license

#ruby #convert #i18n 

Numbers_and_words: Convert Numbers to Words using I18N
Nat  Grady

Nat Grady


Electron-create-menu: A Default Menu for Your Electron Applications


Provides a default menu for your electron applications, with convenience functions for multiplatform use and i18n.


Install using npm install electron-create-menu.


Instead of importing Menu from electron, import it from electron-create-menu:

import Menu from "electron-create-menu";
// or
const Menu = require("electron-create-menu");

To get a default menu with platform-appropriate menu items and submenus, call Menu like so:


Note: This API has to be called after the ready event of app module.

Menu always returns the menu object that Menu.buildFromTemplate creates, so you can access instance methods on it.

Optional arguments

Menu has two optional functions you can pass it

  • The first argument is the callback function, where you can further edit (or replace) the generated menu.
  • The second argument is the i18n function where you can supply a function to use for translating the menu items.
Menu(callback, i18n);

The callback function

callback receives two arguments:

  • The generated menu
  • A function that returns {type: 'separator'} for convenience.

It expects you to return a menu-like object, either the edited default menu or a new menu.

Callback example

To append a menu item to the menu, push an object onto menu and return it:

Menu((defaultMenu, separator) => {
    label: "My custom menu!",
    submenu: [
      { label: "my first item" },
      { label: "my second item" }

  return defaultMenu;

The i18n function

The i18n function is applied to the labels of the default menu. There are two things worth mentioning:

  • Most items in the default menu are specified by a role, so the OS will supply the translation.
  • Labels added in the callback function are not translated by this function.

Example using i18next

const i18next = require('i18next');

  /* assumed setup of i18next here */
}).then(function(t) {

    menu => {
        label: i18next.t('My custom menu!'),
        submenu: [
          {label: i18next.t('my first item')},
          {label: i18next.t('my second item')},

      return menu;

    // This function is used to translate the default labels


Multiplatform use

Each item in your menu can have two new properties, showOn and hideOn. These accept a string or an array of strings that correspond to process.platform values such as 'darwin' or 'win32'.

// this shows the menu item only on macOs
  showOn: "darwin";

// this hides the menu item on windows and macOs
  hideOn: ["win32", "darwin"];

With these, you can adapt your menu to multiple platforms without having to maintain multiple menu templates. See the default template for an example of a consolidated template.

You can also add a string or an array of strings as an argument to the separator function: separator('darwin'). The given value is interpreted as the value for showOn.


Menu((defaultMenu, separator) => {

    label: "My custom menu!",
    submenu: [
        label: 'This is only shown on macOs',
        showOn: 'darwin',
      separator('darwin'), // this is shown only macOs
        'This is hidden on windows'
        hideOn: ['win32']

  return defaultMenu;

Made by @kilianvalkhof

Other projects:

  • 💻 Polypane - Develop responsive websites and apps twice as fast on multiple screens at once
  • 🖌️ Superposition - Kickstart your design system by extracting design tokens from your website
  • 🗒️ FromScratch - A smart but simple autosaving scratchpad

Author: Kilian
Source Code: 
License: ISC license

#electron #menu #javascript #i18n #node 

Electron-create-menu: A Default Menu for Your Electron Applications
Royce  Reinger

Royce Reinger


i18n_data: Ruby - Country/language Names and 2-letter-code Pairs

  • Present users coutries/languages in their language
  • Convert a country/language-name to its 2-letter-code
  • List of 2-letter-code/name pairs for all countries/languages in all languages


Through pkg-isocodes:

  • 185 Language codes (iso 639 - 2 letter) in 68 Languages
  • 246 Country codes (iso 3166 - 2 letter) in 86 Languages
  • contry specific codes e.g. zh_TW are also available, have a look at the isocodes website for all options


gem install i18n_data


require 'i18n_data'
I18nData.languages        # {"DE"=>"German",...}
I18nData.languages('DE')  # {"DE"=>"Deutsch",...}
I18nData.languages('FR')  # {"DE"=>"Allemand",...}

I18nData.countries        # {"DE"=>"Germany",...}
I18nData.countries('DE')  # {"DE"=>"Deutschland",...}

I18nData.language_code('German')       # DE
I18nData.language_code('Deutsch')      # DE
I18nData.language_code('Allemand')     # DE

I18nData.country_code('Germany')       # DE
I18nData.country_code('Deutschland')   # DE

Data Providers

  • FileDataProvider: FAST (default) (loading data from cache-files)
  • LiveDataProvider: SLOW (fetching up-to-date data from svn repos)


  • update FileDataProvider caches after each code-change to make changes available to users rake write_cache_for_file_data_provider
  • FileDataProvider tests might fail if caches are not updates

Alphabetical localized sorting

If you would like to have the countries list sorted alphabetically in different languages there is a gem called sort_alphabetical for that.


  • include other language/country code formats (3-letter codes...) ?
  • parse list of files on isocodes for write_cache instead of hardcoding country-specific ones
  • rake task that checks that the readme is updated with language/country counters

Author: grosser
Source Code: 
License: MIT license

#ruby #i18n #data #language 

i18n_data: Ruby - Country/language Names and 2-letter-code Pairs