generated from insurgent-lab/javascript-lib-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# Gettext | ||
|
||
[![hex.pm badge](https://img.shields.io/badge/Package%20on%20hex.pm-informational)](https://hex.pm/packages/gettext) | ||
[![Documentation badge](https://img.shields.io/badge/Documentation-ff69b4)][docs-gettext] | ||
![CI badge](https://github.com/elixir-gettext/gettext/workflows/CI/badge.svg) | ||
|
||
Gettext is an **internationalization** (i18n) and **localization** (l10n) system commonly used for writing multilingual programs. Gettext is a standard for i18n in different communities, meaning there is a great set of tooling for developers and translators. This project is an implementation of the Gettext system in Elixir. | ||
|
||
## Installation | ||
|
||
Add `:gettext` to your list of dependencies in `mix.exs` (use `$ mix hex.info gettext` to find the latest version): | ||
|
||
```elixir | ||
defp deps do | ||
[ | ||
{:gettext, ">= 0.0.0"} | ||
] | ||
end | ||
``` | ||
|
||
Documentation for `Gettext` is [available on Hex][docs-gettext]. | ||
|
||
## Usage | ||
|
||
To use Gettext, define a Gettext module: | ||
|
||
```elixir | ||
defmodule MyApp.Gettext do | ||
use Gettext, otp_app: :my_app | ||
end | ||
``` | ||
|
||
and invoke the Gettext API, which consists of the `*gettext` macros: | ||
|
||
```elixir | ||
import MyApp.Gettext | ||
|
||
# Simple message | ||
gettext("Here is one string to translate") | ||
|
||
# Plural message | ||
number_of_apples = 4 | ||
ngettext("The apple is ripe", "The apples are ripe", number_of_apples) | ||
|
||
# Domain-based message | ||
dgettext("errors", "Here is an error message to translate") | ||
``` | ||
|
||
Messages in Gettext are stored in Portable Object files (`.po`). Such files must be placed at `priv/gettext/LOCALE/LC_MESSAGES/DOMAIN.po`, where `LOCALE` is the locale and `DOMAIN` is the domain (the default domain is called `default`). | ||
|
||
For example, the message to `pt_BR` of the first two `*gettext` calls in the snippet above must be placed in the `priv/gettext/pt_BR/LC_MESSAGES/default.po` file with contents: | ||
|
||
```pot | ||
msgid "Here is one string to translate" | ||
msgstr "Aqui está um texto para traduzir" | ||
msgid "Here is the string to translate" | ||
msgid_plural "Here are the strings to translate" | ||
msgstr[0] "Aqui está o texto para traduzir" | ||
msgstr[1] "Aqui estão os textos para traduzir" | ||
``` | ||
|
||
`.po` are text-based files and can be edited directly by translators. Some may even use existing tools for managing them, such as [Poedit][poedit] or [poeditor.com][poeditor.com]. | ||
|
||
Finally, because messages are based on strings, your source code does not lose readability as you still see literal strings, like `gettext("here is an example")`, instead of paths like `translate("some.path.convention")`. | ||
|
||
Read the [documentation for the `Gettext` module][docs-gettext-module] for more information on locales, interpolation, pluralization, and other features. | ||
|
||
## Workflow | ||
|
||
Gettext is able to automatically extract messages from your source code, alleviating developers and translators from the repetitive and error-prone work of maintaining message files. | ||
|
||
When extracted from source, Gettext places messages into `.pot` files, which are template files. You can then merge those templates files into message files for each specific locale your application is being currently translated to. | ||
|
||
In other words, the typical workflow looks like this: | ||
|
||
1. Add `gettext` calls to your source code. No need to touch message files | ||
at this point as Gettext will return the given string if no message is | ||
available: | ||
|
||
```elixir | ||
gettext("Welcome back!") | ||
``` | ||
|
||
2. Once changes to the source are complete, automatically sync all existing entries to `.pot` (template files) in `priv/gettext` by running: | ||
|
||
```bash | ||
mix gettext.extract | ||
``` | ||
|
||
3. You can then merge `.pot` files into locale-specific `.po` files: | ||
|
||
```bash | ||
# Merge .pot into all locales | ||
mix gettext.merge priv/gettext | ||
|
||
# Merge .pot into one specific locale | ||
mix gettext.merge priv/gettext --locale en | ||
``` | ||
|
||
It is also possible to both extract and merge messages in one step with `mix gettext.extract --merge`. | ||
|
||
## License | ||
|
||
Copyright 2015 Plataformatec | ||
Copyright 2020 Dashbit | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at: | ||
|
||
> <http://www.apache.org/licenses/LICENSE-2.0> | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
|
||
[docs-gettext]: http://hexdocs.pm/gettext | ||
[docs-gettext-module]: http://hexdocs.pm/gettext/Gettext.html | ||
[poedit]: http://poedit.net/ | ||
[poeditor.com]: https://poeditor.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# Jason | ||
|
||
A blazing fast JSON parser and generator in pure Elixir. | ||
|
||
The parser and generator are at least twice as fast as other Elixir/Erlang libraries | ||
(most notably `Poison`). | ||
The performance is comparable to `jiffy`, which is implemented in C as a NIF. | ||
Jason is usually only twice as slow. | ||
|
||
Both parser and generator fully conform to | ||
[RFC 8259](https://tools.ietf.org/html/rfc8259) and | ||
[ECMA 404](http://www.ecma-international.org/publications/standards/Ecma-404.htm) | ||
standards. The parser is tested using [JSONTestSuite](https://github.com/nst/JSONTestSuite). | ||
|
||
## Installation | ||
|
||
The package can be installed by adding `jason` to your list of dependencies | ||
in `mix.exs`: | ||
|
||
```elixir | ||
def deps do | ||
[{:jason, "~> 1.4"}] | ||
end | ||
``` | ||
|
||
## Basic Usage | ||
|
||
``` elixir | ||
iex(1)> Jason.encode!(%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"}) | ||
"{\"age\":44,\"name\":\"Steve Irwin\",\"nationality\":\"Australian\"}" | ||
|
||
iex(2)> Jason.decode!(~s({"age":44,"name":"Steve Irwin","nationality":"Australian"})) | ||
%{"age" => 44, "name" => "Steve Irwin", "nationality" => "Australian"} | ||
``` | ||
|
||
Full documentation can be found at [https://hexdocs.pm/jason](https://hexdocs.pm/jason). | ||
|
||
## Use with other libraries | ||
|
||
### Postgrex | ||
|
||
Versions starting at 0.14.0 use `Jason` by default. For earlier versions, please refer to | ||
[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#postgrex). | ||
|
||
### Ecto | ||
|
||
Versions starting at 3.0.0 use `Jason` by default. For earlier versions, please refer to | ||
[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#ecto). | ||
|
||
### Plug (and Phoenix) | ||
|
||
Phoenix starting at 1.4.0 uses `Jason` by default. For earlier versions, please refer to | ||
[previous versions of this document](https://github.com/michalmuskala/jason/tree/v1.1.2#plug-and-phoenix). | ||
|
||
### Absinthe | ||
|
||
You need to pass the `:json_codec` option to `Absinthe.Plug` | ||
|
||
```elixir | ||
# When called directly: | ||
plug Absinthe.Plug, | ||
schema: MyApp.Schema, | ||
json_codec: Jason | ||
|
||
# When used in phoenix router: | ||
forward "/api", | ||
to: Absinthe.Plug, | ||
init_opts: [schema: MyApp.Schema, json_codec: Jason] | ||
``` | ||
|
||
## Benchmarks | ||
|
||
Detailed benchmarks (including memory measurements): | ||
https://gist.github.com/michalmuskala/4d64a5a7696ca84ac7c169a0206640d5 | ||
|
||
HTML reports for the benchmark (only performance measurements): | ||
http://michal.muskala.eu/jason/decode.html and http://michal.muskala.eu/jason/encode.html | ||
|
||
### Running | ||
|
||
Benchmarks against most popular Elixir & Erlang json libraries can be executed after | ||
going into the `bench/` folder and then executing `mix bench.encode` and `mix bench.decode`. | ||
A HTML report of the benchmarks (after their execution) can be found in | ||
`bench/output/encode.html` and `bench/output/decode.html` respectively. | ||
|
||
## Differences to Poison | ||
|
||
Jason has a couple feature differences compared to Poison. | ||
|
||
* Jason follows the JSON spec more strictly, for example it does not allow | ||
unescaped newline characters in JSON strings - e.g. `"\"\n\""` will | ||
produce a decoding error. | ||
* no support for decoding into data structures (the `as:` option). | ||
* no built-in encoders for `MapSet`, `Range` and `Stream`. | ||
* no support for encoding arbitrary structs - explicit implementation | ||
of the `Jason.Encoder` protocol is always required. | ||
* different pretty-printing customisation options (default `pretty: true` works the same) | ||
|
||
### Encoders | ||
|
||
If you require encoders for any of the unsupported collection types, I suggest | ||
adding the needed implementations directly to your project: | ||
|
||
```elixir | ||
defimpl Jason.Encoder, for: [MapSet, Range, Stream] do | ||
def encode(struct, opts) do | ||
Jason.Encode.list(Enum.to_list(struct), opts) | ||
end | ||
end | ||
``` | ||
|
||
If you need to encode some struct that does not implement the protocol, | ||
if you own the struct, you can derive the implementation specifying | ||
which fields should be encoded to JSON: | ||
|
||
```elixir | ||
@derive {Jason.Encoder, only: [....]} | ||
defstruct # ... | ||
``` | ||
|
||
It is also possible to encode all fields, although this should be | ||
used carefully to avoid accidentally leaking private information | ||
when new fields are added: | ||
|
||
```elixir | ||
@derive Jason.Encoder | ||
defstruct # ... | ||
``` | ||
|
||
Finally, if you don't own the struct you want to encode to JSON, | ||
you may use `Protocol.derive/3` placed outside of any module: | ||
|
||
```elixir | ||
Protocol.derive(Jason.Encoder, NameOfTheStruct, only: [...]) | ||
Protocol.derive(Jason.Encoder, NameOfTheStruct) | ||
``` | ||
|
||
## Injecting an already encoded JSON inside a to-be-encoded structure | ||
|
||
If parts of the to-be-encoded structure are already JSON-encoded, you can | ||
use `Jason.Fragment` to mark the parts as already encoded, and avoid a | ||
decoding/encoding roundtrip. | ||
|
||
```elixir | ||
already_encoded_json = Jason.encode!(%{hello: "world"}) | ||
Jason.encode!(%{foo: Jason.Fragment.new(already_encoded_json)}) | ||
``` | ||
|
||
This feature is especially useful if you need to cache a part of the JSON, | ||
or if it is already provided by another system (e.g. `jsonb_agg` with Postgres). | ||
|
||
## License | ||
|
||
Jason is released under the Apache License 2.0 - see the [LICENSE](LICENSE) file. | ||
|
||
Some elements of tests and benchmarks have their origins in the | ||
[Poison library](https://github.com/devinus/poison) and were initially licensed under [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/). |