i18n
Version, currently 0.4.16 versions
github.com/crimson-knight/i18n.cr
Internationalization API ( i18n ) for Crystal!
41 stars
0 dependents
License: MIT
Installation
# Add this to your shard.yml
dependencies:
i18n:
github: crimson-knight/i18n.cr
version: ~> 0.4.1Then run:
shards installshard.yml
- Crystal
>= 0.35.0- License
- MIT
- Author
- Seth Tucker <crimsonknightstudios@gmail.com>
Dependencies
This version declares no dependencies.
Documentation
Generated from the source of the current release. The first visit to a release that has never been documented starts its build.
README
# i18n

Internationalization API
## Changes from 0.4.0 to 0.4.1
- Supported Crystal versions of >= 0.35
- add Backend::Yaml#exists? to check whether given translation key exists
- add I18n.exists?
- add docs to I18n module public methods (most of wording was taken from the ruby I18n repo)
- Fixed the iter argument in `translate` to properly return the correct index
Example
```crystal
# Array we are looking into ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
I18n.translate("__formats__.date.day_names", iter: 2) # Returns "Wednesday"
```
## Breaking changes from 0.3 to 0.4
- Pluralization rules are now fully suites [CLDR convention](http://cldr.unicode.org/index/cldr-spec/plural-rules). Specifically `en` pluralization no more returns `zero`
## Breaking changes from 0.2 to 0.3
- The first day of the week is now Monday according to ISO 8601.
- The nil value in `month_names` and `abbr_month_names` was removed.
## Installation
Add this to your application's `shard.yml`:
```yaml
dependencies:
i18n:
github: crimson-knight/i18n.cr
```
## Usage
``` crystal
I18n.translate(
"some.dot.separated.path", # key : String
{attr_to_interpolate: "a"}, # options : Hash | NamedTuple? = nil
"pt", # force_locale : String = nil
2, # count : Numeric? = nil
"default translation", # default : String? = nil
nil # iter : Int? = nil
)
I18n.localize(
Time.utc_now, # object : _
"pt", # force_locale : String = I18n.config.locale
:time, # scope : Symbol? = :number
"long" # format : String? = nil
)
```
### Arguments interpolation
Translation may include argument interpolation. For doing this use regular crystal named interpolation placeholder and pass hash or named tuple as a `options` argument:
```yaml
message:
new: "New message: %{text}"
```
```crystal
# New message: hello
I18n.translate("message.new", {text: "hello"})
I18n.translate("message.new", {:text => "hello"})
I18n.translate("message.new", {"text" => "hello"})
```
Also any extra key-value pair will be ignored and missing one will not cause any exception:
```crystal
I18n.translate("message.new", {message: "hello"}) # New message: %{text}
```
### Configuration
```crystal
require "i18n"
I18n.load_path += ["spec/locales"]
I18n.init # This will load locales from all specified locations
I18n.default_locale = "pt" # default can be set after loading translations
```
There is a [handler](https://github.com/TechMagister/kemalyst-i18n) for Kemalyst that bring I18n configuration.
### Note on YAML Backend
Putting translations for all parts of your application in one file per locale could be hard to manage. You can store these files in a hierarchy which makes sense to you.
For example, your config/locales directory could look like this:
```console
locales
|--defaults
|----en.yml
|----pt.yml
|--models
|----en.yml
|----pt.yml
|--views
|----users
|------en.yml
|------pt.yml
```
This way you can separate model related translations from the view ones. To require all described subfolders at once use `**` - `I18n.load_path += ["locals/**/"]`
#### Date/Time Formats
To localize the time (or date) format you should pass `Time` object to the `I18n.localize`. To pick a specific format path `format` argument:
```crystal
I18n.localize(Time.local, scope: :date, format: :long)
```
> By default `Time` will be localized with `:time` scope.
To specify formats and all need localization information (like day or month names) fill your file in following way:
> NOTE: According to ISO 8601, Monday is the first day of the week
```yaml
__formats__:
date:
formats:
default: '%Y-%m-%d' # is used by default
long: '%A, %d de %B %Y'
month_names: # long month names
- Janeiro
- Fevereiro
- Março
- Abril
- Maio
- Junho
- Julho
- Agosto
- Setembro
- Outubro
- Novembro
- Dezembro
abbr_month_names: # month abbreviations
- Jan
- Fev
# ...
day_names: # fool day names
- Segunda
# ...
abbr_day_names: # short day names
- Seg
# ...
```
Format accepts any crystal `Time::Format` directives. Also following directives will be automatically localized:
| Directive | Description | Key |
|---|---|---|
| `%a` | short day name | `date.abbr_day_names` |
| `%A` | day name | `date.day_names` |
| `%b` | short month name | `date.abbr_month_names` |
| `%B` | month name | `date.month_names` |
| `%p` | am-pm (lowercase) | `time.am`/`time.pm` |
| `%P` | AM-PM (uppercase) | `time.am`/`time.pm` |
#### Pluralization
In many languages — including English — there are only two forms, a singular and a plural, for a given string, e.g. "1 message" and "2 messages". Other languages (Arabic, Japanese, Russian and many more) have different grammars that have additional or fewer plural forms.
The `count` interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:
```yaml
message:
one: "%{count} message"
other: "%{count} messages"
```
```crystal
I18n.translate("message", count: 1) # 1 message
I18n.translate("message", count: 2) # 2 messages
I18n.translate("message", count: 0) # 0 messages
```
> `count` should be passed as argument - not inside of `options`. Otherwise regular translation lookup will be applied.
I18n defines default [CLDR rules](http://cldr.unicode.org/index/cldr-spec/plural-rules) for many locales (see `src/i18n/config/plural_rules`), however they can be overwritten:
```crystal
I18n.plural_rules["ru"] = ->(n : Int32) {
if n == 0
:zero
elsif ((n % 10) == 1) && ((n % 100 != 11))
# 1, 21, 31, 41, 51, 61...
:one
elsif ([2, 3, 4].includes?(n % 10) && ![12, 13, 14].includes?(n % 100))
# 2-4, 22-24, 32-34...
:few
elsif ((n % 10) == 0 || ![5, 6, 7, 8, 9].includes?(n % 10) || ![11, 12, 13, 14].includes?(n % 100))
# 0, 5-20, 25-30, 35-40...
:many
else
:other
end
}
```
```yaml
kid:
zero: 'нет детей'
one: '%{count} ребенок'
few: '%{count} ребенка'
many: '%{count} детей'
other: '%{count} детей'
```
```crystal
I18n.locale = "ru"
I18n.translate("kid", count: 0) # нет детей
I18n.translate("kid", count: 1) # 1 ребенок
I18n.translate("kid", count: 2) # 2 ребенка
I18n.translate("kid", count: 6) # 6 детей
```
#### Iteration
To store several alternative objects under one localization key they could be just listed in the file and later retrieved using `iter` argument:
> NOTE : The first index is `0`
```yaml
__formats__:
date:
day_names: [Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday]
```
```crystal
I18n.translate("__formats__.date.day_names", iter: 2) # >>> "Wednesday"
```
### Embedding translations inside your binary
You can embed translations inside your binary by using the following macro call:
```crystal
I18n::Backend::Yaml.embed(["some/locale/directory", "some/other/locale/directory"])
```
## Development
TODO :
- [ ] Add more backends ( Database, json based, ruby based ( why not ? ))
- [ ] others ( there is always something to add ... or remove )
## Contributing
1. Fork it ( https://github.com/crimson-knight/i18n/fork )
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create a new Pull Request
## Contributors
- [[crimson-knight]](https://github.com/crimson-knight) Seth Tucker (New owner as of July 2021)
- [[imdrasil]](https://github.com/imdrasil) Roman Kalnytskyi
Inspiration taken from:
- https://github.com/whity/crystal-i18n
- https://github.com/mattetti/i18n
Special thank you to [[TechMagister]](https://github.com/Techmagister) for being the original owner and creator of this shard.
Links
This release
- Version
0.4.1- Tagged
- Aug 9, 2021
- Commit
3ace0e59e7f3- Crystal
>= 0.35.0- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/crimson-knight/i18n.cr
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 12, 2026
- Synced
- Aug 12, 2026
- Versions
- 6