{"id":29068581,"url":"https://github.com/mixinnetwork/handsaw","last_synced_at":"2025-06-27T11:08:37.312Z","repository":{"id":39864128,"uuid":"318370389","full_name":"MixinNetwork/handsaw","owner":"MixinNetwork","description":"A tool for generating i18n strings for multiple platforms.","archived":false,"fork":false,"pushed_at":"2024-09-05T09:55:00.000Z","size":15571,"stargazers_count":0,"open_issues_count":2,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-06-24T22:08:32.359Z","etag":null,"topics":["i18n"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","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/MixinNetwork.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-12-04T01:42:06.000Z","updated_at":"2024-09-05T09:54:56.000Z","dependencies_parsed_at":"2022-08-09T15:35:29.310Z","dependency_job_id":null,"html_url":"https://github.com/MixinNetwork/handsaw","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/MixinNetwork/handsaw","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MixinNetwork%2Fhandsaw","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MixinNetwork%2Fhandsaw/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MixinNetwork%2Fhandsaw/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MixinNetwork%2Fhandsaw/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MixinNetwork","download_url":"https://codeload.github.com/MixinNetwork/handsaw/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MixinNetwork%2Fhandsaw/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262244893,"owners_count":23281027,"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":["i18n"],"created_at":"2025-06-27T11:08:35.803Z","updated_at":"2025-06-27T11:08:37.305Z","avatar_url":"https://github.com/MixinNetwork.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# handsaw\n\nA tool for generating i18n strings for multiple platforms.\n\n## Usage\n\nInstall on MacOS with:\n```\n$ curl -L  https://raw.githubusercontent.com/MixinNetwork/handsaw/main/handsaw.rb \u003e handsaw.rb \u0026\u0026 brew install handsaw.rb \u0026\u0026 rm handsaw.rb\n```\n\nFor other platforms, download ZIP from [latest release](https://github.com/MixinNetwork/handsaw/releases/latest) and run: `bin/handsaw` or `bin/handsaw.bat`.\n\n```\n$ handsaw --help\nUsage: handsaw [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  -h, --help  Show this message and exit\n\nCommands:\n  gen   Generate i18n strings from other sources, only xlsx file supported for now\n  read  Read i18n strings from specify platform\n```\n\n### Generate from XLSX file or XML directory\n\n#### XLSX file\nmaintain a xlsx file with the following structure:\n\n|platform|keys|en|zh|ja|ms|\n| --- | --- | --- | --- | --- | --- |\n|Android,iOS,Desktop|key1|value1|value1|value1|value1|\n|Android|key2|value2|value2|value2|value2|\n|iOS|key3|value3|value3|value3|value3|\n|Android,ios,Desktop|key4|value4|value4|value4|value4|\n\nrun command below to generate i18n strings:\n```\n$ handsaw gen -i ~/Downloads/client.xlsx -o ~/Downloads/\n```\n\n#### XML directory\n\nmaintain a xml directory with the following structure:\n- dir\n    - en.xml\n    - zh.xml\n    - ja.xml\n    - ...\n\neach file contains a single language strings, e.g. en.xml, zh.xml, ja.xml, ...\nonly en.xml need attribute `platform` to specify the platform, other files can be ignored.\n```xml\n\u003cresources\u003e\n    \u003cstring name=\"key1\" platform=\"Android,iOS,Desktop\"\u003evalue1\u003c/string\u003e\n    \u003cstring name=\"key2\" platform=\"Android\"\u003evalue2\u003c/string\u003e\n    \u003cstring name=\"key3\" platform=\"iOS\"\u003evalue3\u003c/string\u003e\n    \u003cstring name=\"key4\" platform=\"Android,ios,Desktop\"\u003evalue4\u003c/string\u003e\n\u003c/resources\u003e \n```\nrun command below to generate i18n strings:\n```\n$ handsaw gen -i ~/Downloads/dir -o ~/Downloads/\n```\n\n\nit will generate following files:\n- output\n    - Android\n        - values-en\n          - strings.xml\n        - ...\n        - values-zh\n          - strings.xml\n        - values-ja\n          - strings.xml\n        - values-ms\n          - strings.xml\n    - iOS\n      - en.lproj\n        - Localizable.strings\n      - ...\n      - zh.lproj\n        - Localizable.strings\n      - ja.lproj\n        - Localizable.strings\n      - ms.lproj\n        - Localizable.strings\n    - Flutter\n      - intl_en.arb\n      - intl_ja.arb\n      - intl_zh.arb\n      \nevery file sorted by keys according `a-z case-insensitive`.\n\n#### placeholders\ngeneral platform placeholders use format like `%1$s` `%2$d`, it will generate platform specific placeholders, for example: `%@` `%d` for iOS etc.\nspecial platform can use its own placeholders, for example: iOS use `%@` `%d`, Android use `%1$s` `%2$d`, etc.\n\n#### non-en empty value\niOS platform uses en values to place empty values, other platforms leave them empty.\n\n#### iOS key-type\niOS platform support parameter key-type, default value is 0, which means use the xlsx key as the key, 1 means use the English column as the key.\n```\n$ handsaw gen -k 1\n```\n\n#### Android star tag\nAndroid platform keep '**' in the value, other platforms remove it.\n\n#### plural case\nif you want generate plural format like:\n```xml\n\u003cplurals name=\"number_of_day\"\u003e\n    \u003citem quantity=\"other\"\u003e%1$d days remaining\u003c/item\u003e\n    \u003citem quantity=\"one\"\u003eone day remaining\u003c/item\u003e\n\u003c/plurals\u003e\n```\nyou need name the key as `number_of_day.count` and `number_of_day`\n\ne,g:\n\n|platform|keys|en|zh|\n| --- | --- | --- | --- |\n|Android,iOS,Desktop|continue.count|Continue(%1$s)|继续(%1$s)|\n|Android,iOS,Desktop|number_of_day.count|%1$d days remaining|%1$d 天剩余|\n|Android,iOS,Desktop|number_of_day|one day remaining||\n|Android,iOS,Desktop|participant_count|%1$d participants|%1$d 名成员|\n|Android,iOS,Desktop|participant_count.count|%1$d participants|%1$d 名成员|\n\nwill generate following strings for Android:\n```xml\n\u003cplurals name=\"continue\" tools:ignore=\"UnusedQuantity\"\u003e\n  \u003citem quantity=\"other\"\u003eContinue(%1$s)\u003c/item\u003e\n\u003c/plurals\u003e\n\u003cplurals name=\"number_of_day\"\u003e\n  \u003citem quantity=\"other\"\u003e%1$d days remaining\u003c/item\u003e\n  \u003citem quantity=\"one\"\u003eone day remaining\u003c/item\u003e\n\u003c/plurals\u003e\n\u003cplurals name=\"participant_count\"\u003e\n  \u003citem quantity=\"one\"\u003e%1$d participant\u003c/item\u003e\n  \u003citem quantity=\"other\"\u003e%1$d participants\u003c/item\u003e\n\u003c/plurals\u003e\n\n\u003cplurals name=\"continue\" tools:ignore=\"UnusedQuantity\"\u003e\n  \u003citem quantity=\"other\"\u003e继续（%1$s）\u003c/item\u003e\n\u003c/plurals\u003e\n\u003cplurals name=\"number_of_day\" tools:ignore=\"UnusedQuantity\"\u003e\n  \u003citem quantity=\"other\"\u003e剩余%1$d天\u003c/item\u003e\n\u003c/plurals\u003e\n\u003cplurals name=\"participant_count\" tools:ignore=\"UnusedQuantity\"\u003e\n  \u003citem quantity=\"one\"\u003e%1$d 名成员\u003c/item\u003e\n  \u003citem quantity=\"other\"\u003e%1$d 名成员\u003c/item\u003e\n\u003c/plurals\u003e\n```\nAndroid only 'en' value NOT use tools:ignore=\"UnusedQuantity\" for now, see [plural list](https://github.com/MixinNetwork/handsaw/blob/main/src/main/kotlin/one/mixin/handsaw/generate.kt#L18)\n\nfollowing strings for iOS:\n```\n\"continue_count\" = \"Continue(%1$@)\";\n\"number_of_day_count\" = \"%1$@ days remaining\";\n\"number_of_day\" = \"one day remaining\";\n\"participant_count\" = \"%1$@ participant\";\n\"participant_count_count\" = \"%1$@ participants\";\n\n\"continue_count\" = \"继续（%1$@）\";\n\"number_of_day_count\" = \"剩余%1$@天\";\n\"participant_count\" = \"%1$@ 名成员\";\n\"participant_count_count\" = \"%1$@ 名成员\";\n```\n\nwith `-k 1` option:\n```\n\"Continue(%@)\" = \"Continue(%@)\";\n\"Continue(%@)\" = \"Continue(%@)\";\n\"%d days remaining\" = \"%d days remaining\";\n\"one day remaining\" = \"one day remaining\";\n\"%d participant\" = \"%d participant\";\n\"%d participants\" = \"%d participants\";\n\n\"Continue(%@)\" = \"继续（%@）\";\n\"Continue(%@)\" = \"继续（%@）\";\n\"%d days remaining\" = \"剩余%d天\";\n\"%d participant\" = \"%d 名成员\";\n\"%d participants\" = \"%d 名成员\";\n```\n\nfollowing strings for Flutter:\n```\n{\n\"continueCount\" : \"Continue{arg0}\",\n\"numberOfDayCount\" : \"%1$@ days remaining\",\n\"numberOfDay\" : \"one day remaining\",\n\"participantCount\" : \"%1$@ participant\",\n\"participantCountCount\" : \"%1$@ participants\",\n\n\"continueCount\" : \"继续（%1$@）\",\n\"numberOfDayCount\" : \"剩余%1$@天\",\n\"participantCount\" : \"%1$@ 名成员\",\n\"participantCountCount\" : \"%1$@ 名成员\",\n}\n```\n\n\n### Read from Android platform\n\nrun command below to read i18n strings from Android platform and generate a xlsx file:\n```\n$ handsaw read -i ~/android-app/app/src/main/res/ -o ~/Downloads\n```\nit will generate a client.xlsx file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmixinnetwork%2Fhandsaw","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmixinnetwork%2Fhandsaw","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmixinnetwork%2Fhandsaw/lists"}