Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/Oval-Tutu/smiti18n
A very complete internationalization library for Lua with LÖVE support 🌕💕
https://github.com/Oval-Tutu/smiti18n
i18n l10n love2d lua luajit
Last synced: 2 days ago
JSON representation
A very complete internationalization library for Lua with LÖVE support 🌕💕
- Host: GitHub
- URL: https://github.com/Oval-Tutu/smiti18n
- Owner: Oval-Tutu
- License: mit
- Created: 2025-01-12T00:35:07.000Z (15 days ago)
- Default Branch: master
- Last Pushed: 2025-01-19T11:22:22.000Z (8 days ago)
- Last Synced: 2025-01-19T12:26:15.330Z (7 days ago)
- Topics: i18n, l10n, love2d, lua, luajit
- Language: Lua
- Homepage:
- Size: 545 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- trackawesomelist - smiti18n (⭐16) - A very complete internationalization library for Lua with LÖVE support. (Recently Updated / [Jan 22, 2025](/content/2025/01/22/README.md))
README
# smiti18n
A very complete internationalization library for Lua with LÖVE support 🌕💕
[![Test 🧪](https://github.com/Oval-Tutu/smiti18n/actions/workflows/ci.yml/badge.svg)](https://github.com/Oval-Tutu/smiti18n/actions/workflows/ci.yml) [![Lua Versions](https://img.shields.io/badge/Lua-5.1%20%7C%205.2%20%7C%205.3%20%7C%205.4%20%7C%20JIT-24a6db)](https://github.com/Oval-Tutu/smiti18n) [![LÖVE Versions](https://img.shields.io/badge/L%C3%96VE-11.x-e14895)](https://love2d.org) [![LuaRocks](https://img.shields.io/luarocks/v/flexiondotorg/smiti18n?color=2b3c64)](https://luarocks.org/modules/flexiondotorg/smiti18n) [![License](https://img.shields.io/github/license/Oval-Tutu/smiti18n?color=a01830)](https://github.com/Oval-Tutu/smiti18n/blob/master/LICENSE)
## Introduction
smiti18n (*pronounced smitten*) is a powerful internationalization (i18n) library that helps you create multilingual applications in Lua and [LÖVE](https://love2d.org/).
**Core Features:** 🚀
- 📁 Smart file-based loading & fallbacks
- 🔠 Rich text interpolation & pluralization
- 🌏 Locale-aware formatting for numbers, dates and currency
- 💕 Built for LÖVE game engine**Rich Game Content:** 🎮
- 💬 Complex dialogue support:
- Branching conversations
- Character-specific translations
- Context-aware responses
- 🎯 53 locales, 650+ game-specific phrases
- 📊 36 regional number formatsAn intuitive API for managing translations forked from [i18n.lua](https://github.com/kikito/i18n.lua) by Enrique García Cota incorporating a [collection of community contributions](https://github.com/kikito/i18n.lua/pulls).
The number, date and time formatting has been ported from [Babel](https://github.com/LuaDist-testing/babel). Includes translations from [PolyglotGamedev](https://github.com/PolyglotGamedev). Significantly expanded test coverage and documentation.### Requirements
- Lua 5.1-5.4 or LuaJIT 2.0-2.1
- LÖVE 11.0+ (*optional*)### Quick example
Here's a quick example:
```lua
i18n = require 'smiti18n'-- Load some translations
i18n.load({
en = {
greeting = "Hello %{name}!",
messages = {
one = "You have one new message",
other = "You have %{count} new messages"
}
},
es = {
greeting = "¡Hola %{name}!",
messages = {
one = "Tienes un mensaje nuevo",
other = "Tienes %{count} mensajes nuevos"
}
}
})-- Set the current locale
i18n.setLocale('es')-- Use translations
print(i18n('greeting', {name = "Luna"})) -- ¡Hola Luna!
print(i18n('messages', {count = 3})) -- Tienes 3 mensajes nuevos
```## Installation
### Using LuaRocks
[smiti18n is available on LuaRocks](https://luarocks.org/modules/flexiondotorg/smiti18n). You can install it with the following command:
```shell
luarocks install smiti18n
```### Manual Installation
#### Option 1: Git Clone
Clone this repository and copy the `smiti18n` folder into your project as something like `lib/smiti18n`.
```shell
git clone https://github.com/Oval-Tutu/smiti18n.git
cd smiti18n
cp -r smiti18n your-project/lib/
```#### Option 2: Download Release
1. Download latest release from [Releases](https://github.com/Oval-Tutu/smiti18n/releases)
2. Extract the archive
3. Copy `smiti18n` directory to your projectProject structure after installation:
```
your-project/
├── lib/
│ └── smiti18n/
│ ├── init.lua
│ ├── interpolate.lua
│ ├── plural.lua
│ ├── variants.lua
│ └── version.lua
├── locales/
│ ├── en-UK.lua
│ └── other-locale-files.lua
└── main.lua
```#### Using the Library
```lua
-- Require smiti18n in your code
local i18n = require 'lib.smiti18n'
i18n.loadFile('locales/en-UK.lua')
i18n.setLocale('en-UK')
```### Translation Files
smiti18n supports both single-file and multi-file approaches for managing translations.
#### Single File
Store all translations in one file (e.g., `translations.lua`):
```lua
-- translations.lua
return {
en = {
greeting = "Hello!",
messages = {
one = "You have one message",
other = "You have %{count} messages"
}
},
es = {
greeting = "¡Hola!",
messages = {
one = "Tienes un mensaje",
other = "Tienes %{count} mensajes"
}
}
}-- Load translations
i18n.loadFile('translations.lua')
```#### Multiple Files
Organize translations by language (recommended for larger projects):
Here's an example project structure:
```
locales/
├── en.lua -- English translations
├── es.lua -- Spanish translations
└── fr.lua -- French translations
```**`en.lua`**
```lua
return {
en = { -- Locale key required
greeting = "Hello!",
messages = {
one = "You have one message",
other = "You have %{count} messages"
}
}
}
``````lua
…
i18n.loadFile('locales/en.lua') -- English translation
i18n.loadFile('locales/es.lua') -- Spanish translation
i18n.loadFile('locales/fr.lua') -- French translation
…
```#### Key Points
- Files must include locale key in returned table
- Can be loaded in any order
- Later loads override earlier translations### Locales and Fallbacks
smiti18n provides flexible locale support with automatic fallbacks and regional variants.
#### Locale Naming
- Pattern: `language-REGION` (e.g., 'en-US', 'es-MX', 'pt-BR')
- Separator: hyphen (-) only
- Not supported: underscores, spaces, or other separators#### Fallback Chain
smiti18n implements a robust fallback system:
1. **Current Locale** ('es-MX')
2. **Parent Locales** (if defined, e.g., 'es-419')
3. **Root Locale** ('es')
4. **Default Value** (if provided)
5. **nil** (if no matches found)```lua
-- Example showing fallback chain
i18n.load({
es = {
greeting = "¡Hola!",
},
["es-MX"] = {
farewell = "¡Adiós!"
}
})i18n.setLocale('es-MX')
print(i18n('farewell')) -- "¡Adiós!" (from es-MX)
print(i18n('greeting')) -- "¡Hola!" (from es)
print(i18n('missing')) -- nil (not found)
print(i18n('missing', {
default = 'Not found'
})) -- "Not found" (default value)
```#### Multiple Locales
For handling regional variants, you can specify multiple locales in order of preference:
```lua
i18n.load({
['es-419'] = { cookie = 'galleta' }, -- Latin American
['es-ES'] = { cookie = 'galletita' }, -- European
['es'] = { thanks = 'gracias' } -- Generic
})-- Set multiple locales in priority order
i18n.setLocale({'es-419', 'es-ES', 'es'})i18n('cookie') -- Returns 'galleta' (from es-419)
i18n('thanks') -- Returns 'gracias' (from es)
```Key benefits of multiple locales:
- Handle regional variations (e.g., pt-BR vs pt-PT)
- Share base translations across regions
- Create fallback chains (e.g., es-MX → es-419 → es)
- Support partial translations with automatic fallback**💡NOTE!** Locales are tried in order of preference, with duplicates automatically removed.
### String Interpolation
smiti18n supports three different styles of variable interpolation:
#### Named Variables (*Recommended*)
Named variables are the recommended approach as they make translations more maintainable and less error-prone.
```lua
i18n.set('greeting', 'Hello %{name}, you are %{age} years old')
i18n('greeting', {name = 'Alice', age = 25}) -- Hello Alice, you are 25 years old
```
#### Lua Format Specifiers
```lua
i18n.set('stats', 'Score: %d, Player: %s')
i18n('stats', {1000, 'Bob'}) -- Score: 1000, Player: Bob
```
#### Advanced Formatting
```lua
i18n.set('profile', 'User: %.q | Age: %.d | Level: %.o')
i18n('profile', {
name = 'Charlie',
age = 30,
level = 15
}) -- User: Charlie | Age: 30 | Level: 17k
```Format modifiers:
- `.q`: Quotes the value
- `.d`: Decimal format
- `.o`: Ordinal format### Pluralization
smiti18n implements the [CLDR plural rules](http://cldr.unicode.org/index/cldr-spec/plural-rules) for accurate pluralization across different languages. Each language can have different plural categories like 'one', 'few', 'many', and 'other'.
#### Basic Usage
```lua
i18n = require 'smiti18n'i18n.load({
en = {
msg = {
one = "one message",
other = "%{count} messages"
}
},
ru = {
msg = {
one = "1 сообщение",
few = "%{count} сообщения", -- 2-4 messages
many = "%{count} сообщений", -- 5-20 messages
other = "%{count} сообщения" -- fallback
}
}
})-- English pluralization
i18n.setLocale('en')
print(i18n('msg', {count = 1})) -- "one message"
print(i18n('msg', {count = 5})) -- "5 messages"-- Russian pluralization
i18n.setLocale('ru')
print(i18n('msg', {count = 1})) -- "1 сообщение"
print(i18n('msg', {count = 3})) -- "3 сообщения"
print(i18n('msg', {count = 5})) -- "5 сообщений"
```**💡NOTE!** The `count` parameter is required for plural translations.
#### Custom Pluralization Rules
For special cases or invented languages, you can define custom pluralization rules by specifying a custom pluralization function in the second parameter of `setLocale()`.
```lua
-- Custom pluralization for a constructed language
local customPlural = function(n)
if n == 0 then return 'zero' end
if n == 1 then return 'one' end
if n > 1000 then return 'many' end
return 'other'
endi18n.setLocale('conlang', customPlural)
```This function must return a plural category when given a number.
Available plural categories:
- `zero`: For languages with special handling of zero
- `one`: Singular form
- `two`: Special form for two items
- `few`: For languages with special handling of small numbers
- `many`: For languages with special handling of large numbers
- `other`: Default fallback form### Arrays
Translation values can be arrays for handling ordered collections of strings with support for interpolation and pluralization.
```lua
i18n.load({
en = {
-- Simple array of strings
greetings = {"Hello!", "Hi there!", "Howdy!"},-- Get a random greeting
print(i18n('greetings')[math.random(#i18n('greetings'))])
}
})
```#### Features
Arrays support:
- Plain strings
- Interpolated values
- Plural forms
- Nested arrays
- Mixed content types#### Common Use Cases
1. **Dialogue Systems**
```lua
i18n.load({
en = {
dialogue = {
"Detective: What brings you here?",
"Witness: I saw everything %{time}.",
{
one = "Detective: Just %{count} witness?",
other = "Detective: Already %{count} witnesses."
}
}
}
})-- Play through dialogue sequence
for _, line in ipairs(i18n('dialogue', {time = "last night", count = 1})) do
print(line)
end
```2. **Tutorial Steps**
```lua
i18n.load({
en = {
tutorial = {
"Welcome to %{game_name}!",
{
one = "You have %{lives} life - be careful!",
other = "You have %{lives} lives remaining."
},
"Use WASD to move",
"Press SPACE to jump"
}
}
})
```3. **Status Displays**
```lua
i18n.load({
en = {
status = {
"=== Game Status ===",
"Player: %{name}",
{
one = "%{coins} coin collected",
other = "%{coins} coins collected"
},
"Level: %{level}",
"=================="
}
}
})
```#### Tips
- Arrays maintain their order
- Access individual elements with numeric indices
- Use `#` operator to get array length
- Combine with `math.random()` for random selection
- Arrays can be nested for complex dialogue trees### Formats
The library provides utilities for formatting numbers, prices and dates according to locale conventions. Formats are configured through locale files using the `_formats` key. If no locale-specific formats are defined, the library falls back to ISO standard formats.
```lua
-- Example locale file (en-UK.lua)
return {
["en-UK"] = {
_formats = {
currency = {
symbol = "£",
decimal_symbol = ".",
thousand_separator = ",",
positive_format = "%c %p%q" -- £ 99.99
},
number = {
decimal_symbol = ".",
thousand_separator = ","
},
date_time = {
long_date = "%l %d %F %Y", -- Monday 25 March 2024
short_time = "%H:%M" -- 15:45
}
}
}
}
```#### Usage
```lua
i18n.loadFile('locales/en-UK.lua')
i18n.setLocale('en-UK')-- Numbers
local num = i18n.formatNumber(1234.56) -- "1,234.56"-- Currency
local price = i18n.formatPrice(99.99) -- "£ 99.99"-- Dates
local date = i18n.formatDate("long_date") -- "Monday 25 March 2024"
```#### ISO Fallbacks
When no locale formats are configured, falls back to:
- Numbers: ISO 31-0 (space separator, point decimal) - "1 234.56"
- Currency: ISO 4217 (XXX symbol) - "XXX 99.99"
- Dates: ISO 8601 - "2024-03-25T15:45:30"#### Format Patterns
Dates use [strftime](https://strftime.org/) codes: `%Y` year, `%m` month, `%d` day, `%H` hour, `%M` minute.
- Currency patterns use:
- `%c` - currency symbol
- `%q` - formatted amount
- `%p` - polarity sign (+/-)For a complete reference implementation of all format options, see [spec/en-UK.lua](https://github.com/Oval-Tutu/smiti18n/blob/master/spec/en-UK.lua).
## Contributing
Contributions are welcome! Please feel free to submit a pull request.
## Specs
This project uses [busted](https://github.com/Olivine-Labs/busted) for its specs. If you want to run the specs, you will have to install it first. Then just execute the following from the root inspect folder:
```shell
busted
```