1675898220
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
Click here to see an interactive demo of typesafe-i18n
showing some key aspects of the type-checking capabilities of this internationalization library.
⌨️ 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 presscmd + 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.
npm install typesafe-i18n
The changelog of this project can be found here
5.x.x
: see the release post
4.x.x
: see the release post
3.x.x
: see the release post
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:
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.
See here if you want to learn how you can use typesafe-i18n
to implement your own specific use-case.
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:
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.
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:
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:
typesafe-i18n
angular-service: 1440 bytes gzippedtypesafe-i18n
react-context: 1618 bytes gzippedtypesafe-i18n
solid-context: 1449 bytes gzippedtypesafe-i18n
svelte-store: 1381 bytes gzippedtypesafe-i18n
vue-plugin: 1299 bytes gzippedThe package was optimized for performance:
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.
typesafe-i18n
failsRunning 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
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.
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.
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 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
.
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
.
When you want to dynamically access a translation, you can use the usual JavaScript syntax to access a property via a variable (myObject[myVariable]
).
// 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
<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'
</script>
<h2>{$LL.category[category].title()}
<p>
{$LL.category[category].description()}
<p>
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).
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.
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 useslodash/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
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
}
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) {
showErrorMessage(LL.user.create.nameNotProvided())
return
}
if (isStrongPassword(password)) {
showErrorMessage('Password is too weak')
return
}
// ... 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) {
showErrorMessage(LL.user.create.nameNotProvided())
return
}
if (isStrongPassword(password)) {
showErrorMessage('Password is too weak') // => ERROR: Argument of type 'string' is not assignable to parameter of type 'LocalizedString'.
return
}
// ... create user in DB
}
With the type LocalizedString
you can restrict your functions to only translated strings.
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
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')
intl.__disableRegExpRestore()
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.
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/**"
+ ]
+ },
Author: ivanhofer
Source Code: https://github.com/ivanhofer/typesafe-i18n
License: MIT license
1674917220
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.
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 https://github.com/ever-co/ever-gauzy and https://github.com/ever-co/ever-demand). You are welcome to check more information about the platforms at our official website - https://ever.co.
For quick features review, please see our official docs screenshots page.
Short list of platform features:
For more information check out our official website traduora.co, or our docs at docs.traduora.co.
Any missing feature you'd like to see? File an issue with the feature request to let us know.
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.
Please check out the configuration and deployment documents for more information on deploying Traduora.
Some questions come up over and over again. Be sure to check out our FAQ first!
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 mailto:security@ever.co or on and not by creating a GitHub issue.
We think it's great that you'd like to contribute to Traduora.
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.
Author: ever-co
Source Code: https://github.com/ever-co/ever-traduora
License: AGPL-3.0 license
1674815520
The open-source Calendly alternative.
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 Cal.com 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.
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:
To get a local copy up and running, please follow these simple steps.
Here is what you need to be able to run Cal.
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 https://github.com/calcom/cal.com/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 https://github.com/calcom/cal.com.git
Go to the project folder
cd cal.com
Install packages with yarn
yarn
Set up your .env file
.env.example
to .env
openssl rand -base64 32
to generate a key and add it under NEXTAUTH_SECRET
in the .env file.openssl rand -base64 24
to generate a key and add it under CALENDSO_ENCRYPTION_KEY
in the .env file.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
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
Configure environment variables in the .env
file. Replace <user>
, <pass>
, <db-host>
, <db-port>
with their applicable values
DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>'
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
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 thepackages/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
Pull the current version:
git pull
Check if dependencies got added/updated/removed
yarn
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.
Cal.com, Inc. does not provide official support for Docker, but we will accept fixes and documentation. Use at your own risk.
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.
See the roadmap project for a list of proposed features (and known issues). You can change the view to see planned tagged releases.
Please see our contributing guide.
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.
.../auth/calendar.events
, .../auth/calendar.readonly
and select Update.<Cal.com URL>/api/integrations/googlecalendar/callback
and <Cal.com URL>/api/auth/callback/google
replacing Cal.com URL with the URI at which your application runs.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
<Cal.com URL>/api/auth/callback/google
<Cal.com URL>/api/integrations/office365calendar/callback
replacing Cal.com URL with the URI at which your application runs.ZOOM_CLIENT_ID
and ZOOM_CLIENT_SECRET
fields.<Cal.com URL>/api/integrations/zoomvideo/callback
replacing Cal.com URL with the URI at which your application runs.meeting:write
.DAILY_API_KEY
field in your .env file.DAILY_SCALE_PLAN
variable to true
in order to use features like video recording.HUBSPOT_CLIENT_ID
and HUBSPOT_CLIENT_SECRET
fields.<Cal.com URL>/api/integrations/hubspot/callback
replacing Cal.com URL with the URI at which your application runs.crm.objects.contacts
Special thanks to these amazing projects which help power Cal.com:
Cal.com is an open startup and Jitsu (an open-source Segment alternative) helps us to track most of the usage metrics.
Author: Calcom
Source Code: https://github.com/calcom/cal.com
License: View license
#opensource #typescript #nextjs #postgresql #prisma #tailwindcss
1667344920
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"
end
Or install the gem manually by doing gem install jekyll-polyglot
and specify the plugin using _config.yml
:
plugins:
- 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
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.)The optional lang_from_path: true
option enables getting page language from the first or second path segment, e.g de/first-one.md
, or _posts/zh_Hans_HK/use-second-segment.md
, if the lang frontmatter isn't defined.
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:
_posts/2010-03-01-salad-recipes-en.md
_posts/2010-03-01-salad-recipes-sv.md
_posts/2010-03-01-salad-recipes-fr.md
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:
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.
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/)
becomes
<header class="post-header">
<h1 class="post-title">au sujet de notre entreprise</h1>
</header>
<article class="post-content">
<p>Nous sommes un restaurant situé à Paris . <a href="/fr/menu/">Ceci est notre menu.</a></p>
</article>
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.
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.
becomes
<p>Cliquez <a href="https://mywebsite.com/fr/">ici</a> pour aller à l'entrée du site.
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.
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']
---
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 site.data
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.
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.
default_lang
.{{ site.languages }}
to get an array of your I18n strings.{{ site.default_lang }}
to get the default_lang I18n string.{{ 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
.{{ I18n_Headers https://yourwebsite.com/ }}
to append SEO bonuses to your website.{{ Unrelativized_Link href="/hello" }}
to make urls that do not get influenced by url correction regexes.site.data
localization for efficient rich text replacement.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!
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
parallel_localization: false
in the _config.yml
config.yml
will disable sourcemap generation:sass:
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!
pt_BR
pt_PT
ar
ja
ru
nl
ko
he
Author: untra
Source Code: https://github.com/untra/polyglot
License: MIT license
1665704880
The Translation component provides tools to internationalize your application.
$ 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:
Help Symfony by sponsoring its development!
Author: Symfony
Source Code: https://github.com/symfony/translation
License: MIT license
1665214620
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/")
textdomain("sample")
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 !
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/")
textdomain("sample")
daystr(n) = format(ngettext("{1} day", "{1} days", n), n)
println(daystr(1))
println(daystr(3))
When run, this gives
$ LANGUAGE=fr julia daystr.jl
1 jour
3 jours
Currently this library relies on Python's built-in gettext.py implementation via PyCall. In the future, it may make sense to port this code into a Julia-native version (see issue 1).
Author: Julia-i18n
Source Code: https://github.com/Julia-i18n/Gettext.jl
License: View license
1663066980
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 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.
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
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.
polyglot.extend({
"hello": "Hello"
});
polyglot.t("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.
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).
node.js:
$ npm install babelfish
browser:
$ bower install babelfish
Use es5-shim for old browsers compatibility.
#{varname}
Echoes value of variable((Singular|Plural1|Plural2)):count
Plural formexample:
А у меня в кармане #{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))
Modern javascript i18n localization library based on ES6 tagged templates and the good old GNU gettext.
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 - https://github.com/ttag-org/ttag-cli
npm install --save-dev ttag-cli
https://unpkg.com/ttag/dist/ttag.min.js
This project is designed to work in pair with babel-plugin-ttag.
But you can also play with it without transpilation.
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.
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.
Features
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:
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.
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.
attranslate
supports the following translation-services:
manual
: Translate texts manually by entering them into attranslate
.sync-without-translate
: Does not change the language. This can be useful for converting between file formats, or for maintaining region-specific differences.Thank you for following this article.
i18next Crash Course | the JavaScript i18n framework
1661919268
Manage localization with static analysis.
npm install --save-dev i18n-extract
This module analyses code statically for key usages, such as i18n.t('some.key')
, in order to:
E.g. This module works well in conjunction with:
marker: 'polyglot.t',
)marker: 'i18n',
)i18n('key.static')
i18n('key.' + 'concat')
i18n(`key.template`)
i18n(`key.${dynamic}`)
/* i18n-extract key.comment */
Parse the code
to extract the argument of calls of i18n(key
).
code
should be a string.Example
import {extractFromCode} from 'i18n-extract';
const keys = extractFromCode("const followMe = i18n('b2b.follow');", {
marker: 'i18n',
});
// keys = ['b2b.follow']
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.Example
import {extractFromFiles} from 'i18n-extract';
const keys = extractFromFiles([
'*.jsx',
'*.js',
], {
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.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
.Example
import {findMissing} from 'i18n-extract';
const missing = findMissing({
key1: 'key 1',
}, ['key1', 'key2']);
/**
* missing = [{
* type: 'MISSING',
* key: 'key2',
* }];
*/
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
.Example
import {findUnused} from 'i18n-extract';
const unused = findUnused({
key1: 'key 1',
key2: 'key 2',
}, ['key1']);
/**
* unused = [{
* type: 'UNUSED',
* key: 'key2',
* }];
*/
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.Example
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',
* }];
*/
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
.Example
import {forbidDynamic} from 'i18n-extract';
const forbidDynamic = forbidDynamic({}, ['key.*']);
/**
* forbidDynamic = [{
* type: 'FORBID_DYNAMIC',
* key: 'key.*',
* }];
*/
Flatten the object.
object
should be a object.Example
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',
* };
*/
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.Example
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.
*/
Author: oliviertassinari
Source Code: https://github.com/oliviertassinari/i18n-extract
License: MIT license
1661605560
Dynamically load translation in your app.
Initiate I18n in your dart's main() {}
void main() {
// ...
I18n.init(
url: 'https://www.example.com',
locale: 'hi',
locales: ['en', 'hi', 'pa'],
);
// ...
}
Now you can use I18n.builder anywhere in your code
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
I18n.childBuilder(
'Who am I ?',
(translatedText, child) {
return Column(
children: [
Text(translatedText),
child, // same on both first and second build
],
);
},
Container(), // will be reused in rebuild
),
],
);
Set your server to respond in this way.
Request Method: GET
Request URL: https://www.example.com/hi.json
Response
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: https://www.example.com/hi
{
'en': sourceText,
'target': targetLocale,
}
Response
Content-Type: application/json
{'en': 'How are you ?', 'hi': 'आप कैसे हो ?'}
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
):
dependencies:
dynamic_i18n: ^0.0.6
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:dynamic_i18n/dynamic_i18n.dart';
example/main.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);
@override
_SomeStatefulWidgetState createState() => _SomeStatefulWidgetState();
}
class _SomeStatefulWidgetState extends State<SomeStatefulWidget> {
late I18n locale;
@override
initState() {
locale = I18n(this);
super.initState();
}
@override
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);
@override
_AnotherStatefulWidgetState createState() => _AnotherStatefulWidgetState();
}
class _AnotherStatefulWidgetState extends I18nState<AnotherStatefulWidget> {
@override
build(BuildContext context) {
return Text(i18n('Hello world'));
}
}
// Stateless widget example
class SomeStatelessWidget extends StatelessWidget {
const SomeStatelessWidget({Key? key}) : super(key: key);
@override
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
I18n.childBuilder(
'Who am I ?',
(translatedText, child) {
return Column(
children: [
Text(translatedText),
child, // same on both first and second build
],
);
},
Container(), // will be reused in rebuild
),
],
);
}
}
Author: hsbijarniya
Source Code: https://github.com/hsbijarniya/dynamic_i18n
License: MIT license
1659280020
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.
route('people')
http://site.com/people
http://site.com/fr/people
Linguist works perfectly well with https://github.com/tightenco/ziggy 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.
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();
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.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
//App\Http\Kernel::class
Keevitaja\Linguist\LocalizedKernel::class
);
App\Http\Kernel
Note: This also works with custom root namespace.
<?php
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);
}
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' => [
\Keevitaja\Linguist\LocalizeUrls::class,
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)
{
$linguist->localize();
}
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') }}
http://site.com/people // default locale from linguist config
http://site.com/fr/people
http://site.com/ru/people
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: https://github.com/keevitaja/linguist
License: MIT license
1659254040
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
:
Jenssegers\Date\DateServiceProvider::class,
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 (https://carbon.nesbot.com/docs/#supported-locales):
The Date class extends the Carbon methods such as format
and diffForHumans
, and translates them based on your locale:
use Jenssegers\Date\Date;
Date::setLocale('nl');
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 https://carbon.nesbot.com/docs for more information. Here are some of the available methods:
You can create Date objects just like the DateTime object (http://www.php.net/manual/en/datetime.construct.php):
$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 (http://www.php.net/manual/en/datetime.formats.relative.php):
$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);
You can format a Date object like the DateTime object (http://www.php.net/manual/en/function.date.php):
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
You can manipulate by using the add and sub methods, with relative intervals (http://www.php.net/manual/en/datetime.formats.relative.php):
$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 https://github.com/briannesbitt/Carbon.
To report a security vulnerability, follow these steps.
Author: jenssegers
Source Code: https://github.com/jenssegers/date
License: MIT license
1659246480
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
$newsItem
->setTranslation('name', 'en', 'Name in English')
->setTranslation('name', 'nl', 'Naam in het Nederlands')
->save();
$newsItem->name; // Returns 'Name in English' given that the current app locale is 'en'
$newsItem->getTranslation('name', 'nl'); // returns 'Naam in het Nederlands'
app()->setLocale('nl');
$newsItem->name; // Returns 'Naam in het Nederlands'
composer test
Please see CONTRIBUTING for details.
If you've found a bug regarding security please mail security@spatie.be instead of using the issue tracker.
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: https://github.com/spatie/laravel-translatable
License: MIT license
1659103214
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.
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
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"
21.to_words
=> "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р"
231.to_words
=> "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р"
4030.to_words
=> "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"
=> "չորս հազար երեսուն"
=> "төрт мың отыз"
1000100.to_words
=> "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р жүз"
1000000000000000000000000000000000.to_words
=> "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р жүз отыз үш"]
21.77.to_words
=> "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"
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"]
Russian
Change gender form: (gender: [:female || :male || :neuter])
I18n.with_locale(:ru) { 1001.to_words gender: :neuter }
=> "одна тысяча одно"
Ukranian
Change gender form: (gender: [:female || :male || :neuter])
I18n.with_locale(:ru) { 1001.to_words gender: :neuter }
=> "одна тисяча одне"
Hungarian
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"
Czech
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í"
Spanish
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"
Precision
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
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);
gem install numbers_and_words
See CHANGELOG.md for last changes.
Fork the project. Make your feature addition or bug fix with tests.
Send a pull request. Bonus points for topic branches.
Kirill Lazarev (k.s.lazarev@gmail.com)
Daniel Doubrovkine (http://github.com/dblock)
Sergey Shkirando (shkirando.s@yandex.ru)
Ulrich Sossou (Github, Personal Page)
eLod (http://github.com/eLod)
Mārtiņš Spriņģis (mspringis@gmail.com)
Miks Miķelsons (http://github.com/miks)
Author: kslazarev
Source Code: https://github.com/kslazarev/numbers_and_words
License: MIT license
1658849460
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:
Menu();
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.
Menu has two optional functions you can pass it
callback
function, where you can further edit (or replace) the generated menu.i18n
function where you can supply a function to use for translating the menu items.Menu(callback, i18n);
callback
receives two arguments:
{type: 'separator'}
for convenience.It expects you to return a menu-like object, either the edited default menu or a new menu.
To append a menu item to the menu, push an object onto menu and return it:
Menu((defaultMenu, separator) => {
defaultMenu.push({
label: "My custom menu!",
submenu: [
{ label: "my first item" },
separator(),
{ label: "my second item" }
]
});
return defaultMenu;
});
The i18n function is applied to the labels of the default menu. There are two things worth mentioning:
const i18next = require('i18next');
i18next.init({
/* assumed setup of i18next here */
}).then(function(t) {
Menu(
menu => {
menu.push({
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
i18next.t
);
});
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) => {
defaultMenu.push({
label: "My custom menu!",
submenu: [
{
label: 'This is only shown on macOs',
showOn: 'darwin',
},
separator('darwin'), // this is shown only macOs
{label:
'This is hidden on windows'
hideOn: ['win32']
},
],
});
return defaultMenu;
});
Author: Kilian
Source Code: https://github.com/kilian/electron-create-menu
License: ISC license
1657295640
Through pkg-isocodes:
Install
gem install i18n_data
Usage
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
Development
rake write_cache_for_file_data_provider
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.
TODO
Author: grosser
Source Code: https://github.com/grosser/i18n_data
License: MIT license