{"id":21161581,"url":"https://github.com/loctools/locjson","last_synced_at":"2026-03-19T19:05:25.346Z","repository":{"id":146408164,"uuid":"214293254","full_name":"loctools/locjson","owner":"loctools","description":"A specification for JSON-based translation interchange file format","archived":false,"fork":false,"pushed_at":"2020-08-06T01:44:11.000Z","size":10,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-21T09:29:07.471Z","etag":null,"topics":["file-format","i18n","internationalization","l10n","localization","specification"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/loctools.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-10T22:00:24.000Z","updated_at":"2023-11-26T11:04:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"182b91e4-8c4c-437e-b325-0cda7857292c","html_url":"https://github.com/loctools/locjson","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loctools%2Flocjson","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loctools%2Flocjson/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loctools%2Flocjson/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loctools%2Flocjson/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/loctools","download_url":"https://codeload.github.com/loctools/locjson/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243606956,"owners_count":20318314,"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":["file-format","i18n","internationalization","l10n","localization","specification"],"created_at":"2024-11-20T13:15:10.478Z","updated_at":"2026-01-30T03:06:40.053Z","avatar_url":"https://github.com/loctools.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# LocJSON\n\nThis is a specification for JSON-based translation interchange file format. It can be used in a bilingual mode in place of PO or XLIFF files to send strings for translation, and also as a monolingual resource file format.\n\nThe following requirements were taken into consideration. The file format should be:\n\n1. Easy to parse/serialize (JSON has the widest support among programming languages);\n\n2. Easy to traverse / amend (straightforward structure, no conditional blocks or alternative sub-structures);\n\n3. Easy to read by a human; in canonical key sort order, the overall sequence of keys should make sense;\n\n4. Easy to diff (when used in version control systems);\n\n5. Easy to store large strings (arbitrary line splitting, as in GetText PO files);\n\n6. Preserving unit order (i.e. not a key-value dictionary);\n\n7. Capable of storing multi-line comments;\n\n8. Suitable for bilingual and monolingual use (share the same basic structure);\n\n9. Easy to extend to store extra properties, if needed for external tools.\n\n# Status\n\n-   **DRAFT**. The document is expected to be updated/clarified.\n\n# Terminology\n\n-   **originating application** — an application that creates LocJSON files for translation and that consumes localized copies of such a file.\n-   **translation tool** — a TMS (Translation Management System), CAT (Computer-Aided Translation) tool, or some other tool that takes LocJSON files and updates them with translations.\n\n# Specification\n\n1. LocJSON files are always pretty-printed.\n2. Indentation is always 4 spaces.\n3. All keys in dictionaries are always sorted alphabetically.\n4. All line breaks inside strings are represented with a single `\\n` symbol (Unix-style).\n5. LocJSON files use the `.locjson` file extension.\n\n## Top-level structure\n\nThe top-level structure represents a dictionary with exactly two keys:\n\n```json\n{\n    \"properties\" : {\n        ...\n    },\n    \"units\" : [\n        ...\n    ]\n}\n```\n\n1. `properties` [optional, dictionary] — information related to the file as a whole.\n2. `units` [required, array] — an ordered list of translatable units (segments).\n\n### Top-level `properties` block contents\n\nThis dictionary stores information related to the file as a whole:\n\n```json\n    \"properties\": {\n        \"comments\": [\"This file was generated by AwesomeTool\"],\n        \"version\": 1\n    }\n```\n\n1. `comments` [optional, array of strings] — a list of file-wide comments. To render a multiline comment string, the array is concatenated with a single newline (`\\n`) character.\n2. `version` [optional, number] — the LocJSON file format version. If omitted, `1` is implied.\n\n#### Notes on versioning\n\nFile format version is always an integer number, and will only increase if any backward-incompatible changes are introduced. But the intent is to always stay at version `1` and not introduce any backward-incompatible changes.\n\n### Top-level `units` block\n\nEach unit in this array is a dictionary:\n\n```json\n        ...\n        {\n            \"key\": \"someUniqueKey\",\n            \"properties\": {\n                ...\n            },\n            \"source\": [\n                \"Line 1\\n\",\n                \"Line 2\\n\",\n                \"\\n\",\n                \"A very very very long line split into several \",\n                \"based on a 50-character limit.\"\n            ]\n        },\n        ...\n```\n\n1. `key` [required, string] — a unique (within that file) identifier of the string.\n2. `properties` [optional, dictionary] — properties of a particular unit.\n3. `source` [required\u003csup\u003e1\u003c/sup\u003e, array] — an array of strings that, if concatenated together, comprises a source text to translate. Lines must be split after `\\n`. It is also recommended to split long strings into multiple ones, each of 50 symbols or less, including two symbols for the line break symbol (`\\n`).\n4. `target` [optional\u003csup\u003e1\u003c/sup\u003e, array] — an array of strings that, if concatenated together, comprises a target (translated) text. The same line splitting rules apply as with `source`.\n\n\u003csup\u003e1\u003c/sup\u003e In bilingual use, it is expected to have both `source` and `target` fields present, and translation goes into `target` field; in monolingual use, a translation tool must put the translation back into `source` field when generating a localized copy of a file and omit the `target` field.\n\n### Per-unit `properties` block contents\n\nThis dictionary stores information related to the unit:\n\n```json\n            \"properties\": {\n                \"comments\": [\n                    \"Unit comment line 1\",\n                    \"Unit comment line 2\"\n                ]\n            }\n```\n\n1. `comments` [optional, array of strings] — a list of unit-specific comments. To render a multiline comment string, the array is concatenated with a single newline (`\\n`) character.\n\n## Application-specific extensions\n\nAn application that generates a LocJSON file for translation may want to pass some extra proprietary information in the file. This is allowed only in `properties` dictionaries (both at top level and for each unit). All application-specific keys must start with an `x-` prefix, followed by a tool-specific sub-prefix, forming a namespace for that particular tool.\n\nConsider the following example, where an imaginary tool, _AwesomeTool_, adds its own internal identifiers that start with `x-awesometool-` prefix:\n\n```json\n{\n    \"properties\": {\n        \"comments\": [\"This file was generated by AwesomeTool 2.34\"],\n        \"x-awesometool-generator-version\": \"2.34\",\n        \"x-awesometool-file-id\": \"xf-12345\",\n        \"x-awesometool-original-filename\": \"resources.js\"\n    },\n    \"units\": [\n        {\n            \"key\": \"testKey\",\n            \"properties\": {\n                \"comments\": [\"This is a comment.\"],\n                \"x-awesometool-unit-id\": \"xu-65432\"\n            },\n            \"source\": [\"Some text.\"]\n        }\n    ]\n}\n```\n\n# Translation tool behavior\n\nA translation tool must keep the entire structure of a LocJSON file intact. It is only allowed to add, remove, or modify the contents of a `target` array in each unit definition (or only modify the `source` array in case of monolingual use).\n\nA translation tool may read and use other properties, including the ones that start with `x-` (for example, show them in the translation UI as an additional context).\n\nA translation tool must not add any custom properties, reorder units, or reformat the contents of `source` or `target` array in any unit unless it modifies a particular translation for that unit.\n\nA translation tool may only modify it's own known properties (i.e. the ones that _pre-existed_ in the LocJSON file). This gives an originating application that generates a LocJSON file to control the set of properties it supports, and ensures it can parse the returned file. For example, if LocJSON is generated for an imaginary translation tool `Foo`, and it is known that this tool supports a property `x-foo-fuzzy` (which also has an equivalent in an originating application), then an originating application can include `x-foo-fuzzy` in LocJSON file, and this property will become a part of a contract between an originating application and a translation tool.\n\nA translation tool may be explicitly instructed to remove all `properties` keys (both file-level and unit-level) upon generating a localized version of a file. This reduces the file size of all resources and keeps only the minimal data needed (an array of units with keys and translations; see the _Minimal example_ section below). A translation tool should never remove `properties` keys by default.\n\n# Minimal example\n\nGiven the optional nature of `properties` blocks, a minimal generated LocJSON file would look like this:\n\n```json\n{\n    \"units\": [\n        {\n            \"key\": \"key1\",\n            \"source\": [\"String 1\"],\n        },\n        {\n            \"key\": \"key2\",\n            \"source\": [\"String 2\"],\n        },\n        ...\n    ]\n}\n```\n\n## Bilingual use\n\nIn a bilingual use, a localized copy returned by a translation tool would look like this:\n\n```json\n{\n    \"units\": [\n        {\n            \"key\": \"key1\",\n            \"source\": [\"String 1\"],\n            \"target\": [\"Translated string 1\"],\n        },\n        {\n            \"key\": \"key2\",\n            \"source\": [\"String 2\"],\n            \"target\": [\"Translated string 2\"],\n        },\n        ...\n    ]\n}\n```\n\n## Monolingual use\n\nIn a monolingual use, a localized copy returned by a translation tool (with translations written directly into `source`) would look like this:\n\n```json\n{\n    \"units\": [\n        {\n            \"key\": \"key1\",\n            \"source\": [\"Translated string 1\"],\n        },\n        {\n            \"key\": \"key2\",\n            \"source\": [\"Translated string 2\"],\n        },\n        ...\n    ]\n}\n```\n\nThere are two reasons translations are written directly into `source` in this mode:\n\n1. The structure between source and localized files stays the same to simplify resource file handling.\n2. A localized file can immediately serve as a source file to localize it into other languages (for example, a Chinese source file is translated into English first, and English one is then translated into all other languages).\n\n# Full example\n\nHere's a fuller example of LocJSON file that has comments:\n\n```json\n{\n    \"properties\": {\n        \"comments\": [\"This file was generated by AwesomeTool\"],\n        \"version\": 1\n    },\n    \"units\": [\n        {\n            \"key\": \"welcomeMessage\",\n            \"properties\": {\n                \"comments\": [\"{USER} here is replaced with the first name of a signed in user\"]\n            },\n            \"source\": [\"Hello, {USER}!\"]\n        },\n        {\n            \"key\": \"signInButtonCaption\",\n            \"properties\": {\n                \"comments\": [\"https://example.com/preview/sign-in-dialog-screenshot.png\"]\n            },\n            \"source\": [\"Sign In\"]\n        },\n        {\n            \"key\": \"signInFooterText\",\n            \"properties\": {\n                \"comments\": [\n                    \"This text is displayed below the sign in form.\",\n                    \"https://example.com/preview/sign-in-dialog-screenshot.png\"\n                ]\n            },\n            \"source\": [\n                \"Please read our \u003ca \",\n                \"href=\\\"https://example.com/legal/pp\\\"\u003ePrivacy \",\n                \"Policy\u003c/a\u003e and \u003ca \",\n                \"href=\\\"https://example.com/legal/tos\\\"\u003eTerms of \",\n                \"Service\u003c/a\u003e\"\n            ]\n        }\n    ]\n}\n```\n\n# Mapping LocJSON files to languages\n\nLocJSON format intentionally has no built-in notion of _source language_ or _target language_ properties. It is expected that these properties are implied from the folder structure with LocJSON files, or from file names themselves, and are a part of a contract between an originating application and a translation tool.\n\nConsider the following example folder structure:\n\n```\nen/\n    login.locjson\n    main.locjson\n    settings.locjson\nru/\n    login.locjson\n    main.locjson\n    settings.locjson\nzh-hans/\n    login.locjson\n    main.locjson\n    settings.locjson\n```\n\nHere the files have the same name, but belong to a unique top-level language folder. Now if the agreement is to take English files from `en` folder and put them into corresponding target language folders, the question of source and target language is answered automatically.\n\nAnother example:\n\n```\nlogin/\n    en.locjson\n    ru.locjson\n    zh-hans.locjson\nmain/\n    en.locjson\n    ru.locjson\n    zh-hans.locjson\nsettings/\n    en.locjson\n    ru.locjson\n    zh-hans.locjson\n```\n\nHere localization files are split by a component, but the language is implied from the file name itself.\n\nBoth examples are equally acceptable for both bilingual and monolingual use. In all scenarios, being it a manual or automated localization process, forcing a certain file and folder naming convention to determine source and target languages (as opposed to specifying them as properties within a LocJSON file and relying solely on these properties), is expected to lead to a better discipline and a cleaner localization process overall.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floctools%2Flocjson","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Floctools%2Flocjson","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floctools%2Flocjson/lists"}