{"id":13872299,"url":"https://github.com/miroslavkovac/Lingo","last_synced_at":"2025-07-16T02:30:30.377Z","repository":{"id":63918580,"uuid":"100796831","full_name":"miroslavkovac/Lingo","owner":"miroslavkovac","description":"Powerful Swift string localization library with support for pluralization and string interpolation.","archived":false,"fork":false,"pushed_at":"2022-12-09T14:39:22.000Z","size":83,"stargazers_count":62,"open_issues_count":2,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-13T03:47:28.703Z","etag":null,"topics":["localization","pluralization","server","swift","vapor"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/miroslavkovac.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-19T13:41:53.000Z","updated_at":"2024-11-01T12:59:06.000Z","dependencies_parsed_at":"2023-01-14T14:00:24.660Z","dependency_job_id":null,"html_url":"https://github.com/miroslavkovac/Lingo","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miroslavkovac%2FLingo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miroslavkovac%2FLingo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miroslavkovac%2FLingo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miroslavkovac%2FLingo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/miroslavkovac","download_url":"https://codeload.github.com/miroslavkovac/Lingo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226095643,"owners_count":17572969,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["localization","pluralization","server","swift","vapor"],"created_at":"2024-08-05T23:00:39.106Z","updated_at":"2024-11-23T20:30:50.767Z","avatar_url":"https://github.com/miroslavkovac.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"lingo.png\" alt=\"Lingo logo\" /\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/swift-5-brightgreen.svg\" alt=\"swift 5\"/\u003e\n\t\u003cimg src=\"http://img.shields.io/badge/license-MIT-brightgreen.svg\" alt=\"MIT License\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e\n  • \u003ca href=\"#setup\"\u003eSetup\u003c/a\u003e\n  • \u003ca href=\"#usage\"\u003eUsage\u003c/a\u003e\n  • \u003ca href=\"#performance\"\u003ePerformance\u003c/a\u003e\n  • \u003ca href=\"#license\"\u003eLicense\u003c/a\u003e\n\u003c/p\u003e\n\n**Lingo** is a pure Swift localization library ready to be used in Server Side Swift project but not limited to those. \n\n## Features\n\n* **Pluralization** - including custom language specific pluralization rules (CLDR compatible)\n* **String interpolation**\n* **Flexible data source** (read localizations from a JSON file, database or whatever suites your workflow the best)\n* **Default locale** - if the localization for a requested locale is not available, it will fallback to the default one\n* **Locale validation** - the library will warn you for using invalid locale identifiers (`en-fr` instead of `en-FR` etc.)\n\n## Setup\n\nThere are two ways of integrating Lingo depending on whether you use Vapor or not:\n\n### With Vapor\n\nIf you are using Vapor, we encourage you to use [LingoProvider](https://github.com/vapor-community/Lingo-Provider) which will provide seamless and native integration with Vapor. This way `Lingo` becomes part of `Droplet` and you will be able to get localizations even easier:\n\n```swift\nlet localizedTitle = droplet.lingo.localize(\"welcome.title\", locale: \"en\")\n```\n\n\u003e LingoProvider is a separate package and and can be downloaded from [GitHub](https://github.com/vapor-community/Lingo-Provider). If you use LingoProvider you don't need the Lingo package dependency.\n\n### Without Vapor\n\nAdd the dependency:\n\n```swift\ndependencies: [\n\t...,\n\t.Package(url: \"https://github.com/miroslavkovac/Lingo.git\", majorVersion: 4)\n]\n```\n\nCreate an instance of `Lingo` object passing the root directory path where the localization files are located:\n\n```swift\nlet lingo = try Lingo(rootPath: \"path/to/localizations\", defaultLocale: \"en\")\n```\n\n## Upgrading from version 3 to version 4\n\nIn the version 4 the format of locale identifiers was changed to match [RFC 5646](https://datatracker.ietf.org/doc/html/rfc5646). The version 3 used `_` to separate _language code_ and _country code_, and now the version 4 uses `-`. \n\nIf you were using any locales which include a country code, you would need to rename related translation files to match the new format.    \n\n## Usage\n\nUse the following syntax for defining localizations in a JSON file:\n\n```swift\n{\n\t\"title\": \"Hello Swift!\",\n\t\"greeting.message\": \"Hi %{full-name}!\",\n\t\"unread.messages\": {\n\t\t\"one\": \"You have an unread message.\",\n\t\t\"other\": \"You have %{count} unread messages.\"\n\t}\n}\n```\n\n\u003e Note that this syntax is compatible with `i18n-node-2`. This is can be useful in case you are using a 3rd party localization service which will export the localization files for you.\n\n### Localization\n\nYou can retrieve localized string like this:\n\n```swift\nlet localizedTitle = lingo.localize(\"title\", locale: \"en\")\n\nprint(localizedTitle) // will print: \"Hello Swift!\"\n```\n\n### String interpolation\n\nYou can interpolate the localized strings like this:\n\n```swift\nlet greeting = lingo.localize(\"greeting.message\", locale: \"en\", interpolations: [\"full-name\": \"John\"])\n\nprint(greeting) // will print: \"Hi John!\"\n```\n\n### Pluralization\n\nLingo supports all Unicode plural categories as defined in [CLDR](http://cldr.unicode.org/index/cldr-spec/plural-rules):\n\n* zero\n* one\n* two\n* few\n* many\n* other\n\nExample:\n\n```swift\nlet unread1 = lingo.localize(\"unread.messages\", locale: \"en\", interpolations: [\"count\": 1])\nlet unread24 = lingo.localize(\"unread.messages\", locale: \"en\", interpolations: [\"count\": 24]) \n\nprint(unread1) // Will print: \"You have an unread message.\"\nprint(unread24) // Will print: \"You have 24 unread messages.\"\n```\n\nEach language contains custom pluralization rules that define which plural category should be used for which numeric value. Lingo currently implements rules for the following languages:\n\u003e ak, am, ar, az, be, bg, bm, bn, bo, br, bs, ca, cs, cy, da, de\\-AT, de\\-CH, de\\-DE, de, dz, el, en\\-AU, en\\-CA, en\\-GB, en\\-IN, en\\-NZ, en, eo, es\\-419, es\\-AR, es\\-CL, es\\-CO, es\\-CR, es\\-EC, es\\-ES, es\\-MX, es\\-NI, es\\-PA, es\\-PE, es\\-US, es\\-VE, es, et, eu, fa, ff, fi, fil, fr\\-CA, fr\\-CH, fr\\-FR, fr, ga, gd, gl, gv, he, hi\\-IN, hi, hr, hsb, hu, id, ig, ii, it\\-CH, it, iu, ja, jv, ka, kab, kde, kea, km, kn, ko, ksh, kw, lag, ln, lo, lt, lv, mg, mk, ml, mn, mr\\-IN, ms, mt, my, naq, nb, ne, nl, nn, nso, or, pa, pl, pt, pt-BR, ro, ru, sah, se, ses, sg, shi, sk, sl, smn, sr, sv\\-SE, sv, sw, th, ti, to, tr, tzm, uk, ur, vi, wa, wo, yo, zh\\-CN, zh\\-HK, zh\\-TW, zh\n\nThe original seed of pluralization rules was translated from [Rails i18n](https://github.com/svenfuchs/rails-i18n/tree/master/rails/pluralization) into Swift.\n\n## Performance\n\nIn tests with a set of 1000 localization keys including plural forms, the library was able to handle:\n\n* ~ 1M non interpolated localizations per second or 0.001ms per key.\n* ~ 110K interpolated localizations per second or 0.009ms per key.\n\n\u003e String interpolation uses regular expressions under the hood, which can explain the difference in performance. All tests were performed on i7 4GHz CPU.\n\n## Custom localizations data source\n\nAlthough most of the time, the localizations will be defined in the JSON file, but if you prefer keeping them in a database, we've got you covered!\n\nTo implement a custom data source, all you need is to have an object that conforms to the `LocalizationDataSource` protocol:\n\n```swift\npublic protocol LocalizationDataSource {   \n\n    func availableLocales() throws -\u003e [LocaleIdentifier]\n    func localizations(forLocale: LocaleIdentifier) throws -\u003e [LocalizationKey: Localization]\n    \n}\n```\n\nSo, let's say you are using MongoDB to store your localizations, all you need to do is to create a data source and pass it to Lingo's designated initializer:\n\n```swift\nlet mongoDataSource = MongoLocalizationDataSource(...)\nlet lingo = try Lingo(dataSource: mongoDataSource, defaultLocale: \"en\")\n```\n\nLingo already includes `FileDataSource` conforming to this protocol, which, as you might guess, is wired up to the Longo's convenience initializer with `rootPath`.\n\n## Note on locale identifiers\n\nAlthough it is completely up to you how you name the locales, there is an easy way to get the list of all locales directly from `Locale` class:\n\n```swift\nimport Foundation\n\nprint(Locale.availableIdentifiers)\n```\n\nJust keep that in mind when adding a support for a new locale.\n\n## Limitations\n\nCurrently the library doesn't support the case where different plural categories should be applied to different parts of the *same* localization string. For example, given the definition:\n\n```\n{\n    \"key\": {\n        \"one\": \"You have 1 apple and 1 orange.\",\n        \"other\": \"You have %{apples-count} apples and %{oranges-count} oranges.\"\n    }\n}\n```\n\nand passing numbers 1 and 7:\n\n```swift\nprint(lingo.localize(\"key\", locale: \"en\", interpolations: [\"apples-count\": 1, \"oranges-count\": 7]))\n\n```\n\nwill print:\n\n```\nYou have 1 apple and 7 orange.\n```\n\u003e Note the missing *s* in the printed message.\n\nThis was done on purpose, and the reason for this was to keep the JSON file syntax simple and elegant (in contrast with iOS .stringsdict file and similar). If you still need to support a case like this, a possible workaround would be to split that string in two and combine it later in code.\n\n## Further work\n\n- Locale fallbacks, being RFC4647 compliant.\n- Options for doubling the length of a localized string, which can be useful for debugging.\n- Implement debug mode for easier testing and finding missing localizations.\n- Support for non integer based pluralization rules\n\n## License\n\nMIT License. See the included LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiroslavkovac%2FLingo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmiroslavkovac%2FLingo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiroslavkovac%2FLingo/lists"}