{"id":21257806,"url":"https://github.com/maroontress/pourover","last_synced_at":"2025-03-15T06:23:26.507Z","repository":{"id":103784538,"uuid":"398270113","full_name":"maroontress/PourOver","owner":"maroontress","description":"PourOver is a command-line tool that diagnoses string resources stored in CSV files expressed in multiple languages by comparing the tokens embedded in each string between the languages.","archived":false,"fork":false,"pushed_at":"2021-08-26T16:07:30.000Z","size":29,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-21T21:32:02.485Z","etag":null,"topics":["csharp","dotnet-core","global-tool","i18n","lint","string-resources"],"latest_commit_sha":null,"homepage":"https://maroontress.github.io/PourOver/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maroontress.png","metadata":{"files":{"readme":"README.ja_JP.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":"2021-08-20T12:32:34.000Z","updated_at":"2021-08-26T16:07:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"0a72d942-f642-4a2e-85ea-635e7048dbb5","html_url":"https://github.com/maroontress/PourOver","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maroontress%2FPourOver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maroontress%2FPourOver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maroontress%2FPourOver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maroontress%2FPourOver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maroontress","download_url":"https://codeload.github.com/maroontress/PourOver/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243691257,"owners_count":20331932,"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":["csharp","dotnet-core","global-tool","i18n","lint","string-resources"],"created_at":"2024-11-21T04:05:56.044Z","updated_at":"2025-03-15T06:23:26.501Z","avatar_url":"https://github.com/maroontress.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PourOver\r\n\r\nPourOverはコマンドラインツールで、CSVファイルに格納された複数の言語で表現された文字列リソースに対して、各文字列に組み込まれたトークンが適切かどうかを言語間で比較して診断します。\r\n\r\n## CSVファイルの構造\r\n\r\nCSVファイルはUTF-8でエンコードされ、1行目はヘッダーになります。ヘッダーの一番左のフィールドは使用されません。それ以外のフィールドは、各列の言語名を記載します。ヘッダーは診断の対象ではありませんが、一番左のフィールドを除き、フィールドの内容を診断メッセージで使用します。\r\n\r\nヘッダーに続く、二行目以降の行は、メッセージのIDと、各言語毎の文字列になります。次のような内容の文字列リソースを例とします:\r\n\r\n| ID      | `English`    | `Japanese`   |\r\n| :--     | :--          | :--          |\r\n| `HELLO` | `Hello`      | `こんにちは`  |\r\n| `BYE`   | `Bye`        | `さようなら`  |\r\n\r\nCSVファイルは以下のようになります:\r\n\r\n```plaintext\r\nID,English,Japanese\r\nHELLO,Hello,こんにちは\r\nBYE,Bye,さようなら\r\n```\r\n\r\n## トークン\r\n\r\nトークンは文字列リソースに組み込まれたプレイスホルダーで、ブレース（`{`と`}`）で囲まれた文字列です。例えば、次のような内容の文字列リソースがあるとします:\r\n\r\n| ID     | `English`              | `Japanese`           |\r\n| :--    | :--                    | :--                  |\r\n| `TIME` | `It's {hour} o'clock.` | `{hour}時です。`      |\r\n| `DEAR` | `Dear {name},`         | `拝啓 {name} さん、`  　|\r\n\r\n`TIME`の文字列リソースでは、どの言語においてもトークン`{hour}`が組み込まれ、表示する際に`{hour}`は現在の時刻の（時分秒の）時に置き換えられます。また、`DEAR`では、同様に`{name}`が組み込まれ、表示する際に`{name}`は人名に置き換えられます。\r\n\r\n文字列リソースはトークンを複数含むことができます。\r\n\r\n## 診断メッセージ\r\n\r\n診断メッセージは次のような形式になります:\r\n\r\n\u003e _ファイル名_ `:` _行番号_ `: ` _ID_ `: ` _メッセージ_\r\n\r\n`--verbose`を指定すると次のような形式になります:\r\n\r\n\u003e _ファイル名_ `:` _行番号_ `: ` _ID_ `: （` _診断ID_ `) ` _メッセージ_\r\n\r\n## トークンの診断\r\n\r\n多くの場合、トークンは言語が変わっても、出現する個数は変わりません（出現順序が異なることはあります）。例えば、ある文字列リソースで、言語 _A_ がトークン`{foo}`と`{bar}`を含み、言語 _B_ がトークン`{foo}`と`{baz}`を含むなら、何か間違っている可能性があります（もちろん、例外的に間違っていない場合もあります）。このように、簡単なヒューリスティックスを使って、トークンを診断することが可能です。\r\n\r\n次のト－クンの診断があります:\r\n\r\n- TypeNumberMismatch\r\n- StrayToken\r\n- FrequencyMismatch\r\n\r\n### TypeNumberMismatch\r\n\r\nある文字列リソースで、トークンの種類の数が言語によって異なる場合に報告します。例えば、言語 _A_ は`{foo}`、`{bar}`、`{baz}`の三種類のトークンを含み、言語 _B_ は`{foo}`、`{bar}`の二種類のトークンしか含まない場合が該当します。同じトークンが複数回出現しても、種類としては1つとして数えます。\r\n\r\n例えば、次のような文字列リソースを考えます:\r\n\r\n| ID         | `English`           | `Japanese`              |\r\n| :--        | :--                 | :--                     |\r\n| `EXAMPLE1` | `Hello {foo} {bar}` | `こんにちは {foo}`       |\r\n| `EXAMPLE2` | `Bye {foo} {bar}`   | `さようなら {foo} {foo}` |\r\n\r\nこのとき、次のような診断を出力します（ロケールが英語の場合）:\r\n\r\n```plain\r\nfile.csv:2: EXAMPLE1: The number of unique tokens is different: 'English' has 2 token(s) but 'Japanese' has 1 token(s).\r\nfile.csv:3: EXAMPLE2: The number of unique tokens is different: 'English' has 2 token(s) but 'Japanese' has 1 token(s).\r\n```\r\n\r\nなお、このTypeNumberMismatchが診断された場合、そのフィールドに対しては以降の診断を実施しません。\r\n\r\n### StrayToken\r\n\r\nある文字列リソースで、トークンの種類が言語によって異なる場合に報告します。例えば、言語 _A_ は`{foo}`、`{bar}`のトークンを含み、言語 _B_ は`{foo}`、`{baz}`のトークンを含む場合が該当します。\r\n\r\n例えば、次のような文字列リソースを考えます:\r\n\r\n| ID         | `English`           | `Japanese`              |\r\n| :--        | :--                 | :--                     |\r\n| `EXAMPLE1` | `Hello {foo} {bar}` | `こんにちは {foo} {baz}` |\r\n\r\nこのとき、次のような報告を出力します（ロケールが英語の場合）:\r\n\r\n```plaintext\r\nfile.csv:2: EXAMPLE1: Token {bar} appears only in 'English'.\r\nfile.csv:2: EXAMPLE1: Token {baz} appears only in 'Japanese'.\r\n```\r\n\r\nなお、このStrayTokenの診断を報告した場合は、以降の診断を実施しません。\r\n\r\n### FrequencyMismatch\r\n\r\nある文字列リソースで、特定のトークンの出現回数が言語によって異なる場合に報告します。例えば、言語 _A_ はトークン`{foo}`を一つだけ含み、言語 _B_ はトークン`{foo}`を二つ含む場合が該当します。\r\n\r\n例えば、次のような文字列リソースを考えます:\r\n\r\n| ID         | `English`           | `Japanese`              |\r\n| :--        | :--                 | :--                     |\r\n| `EXAMPLE1` | `Hello {foo} {foo}` | `こんにちは {foo}`       |\r\n\r\nこのとき、次のような報告を出力します（ロケールが英語の場合）:\r\n\r\n```plaintext\r\nfile.csv:2: EXAMPLE1: Token {foo} appears 2 time(s) in 'English' but appears 1 time(s) in 'Japanese'.\r\n```\r\n\r\n## そのほかの診断\r\n\r\nそのほか次の診断があります:\r\n\r\n- InvalidToken\r\n- DuplicateID\r\n\r\n### InvalidToken\r\n\r\nフィールド中のブレース（`{`と`}`）の対応が間違っていて、トークンをパースできないときに報告します。例えば、次のような文字列リソースを考えます:\r\n\r\n| ID         | `English`              | `Japanese`              |\r\n| :--        | :--                    | :--                     |\r\n| `EXAMPLE1` | `Good morning {foo}`   | `おはようございます {foo` |\r\n| `EXAMPLE2` | `Good afternoon {foo}` | `こんにちは foo}`        |\r\n| `EXAMPLE3` | `Good evening {f{oo}`  | `こんばんは {foo}`       |\r\n\r\nこのとき、次のような報告を出力します（ロケールが英語の場合）:\r\n\r\n```plaintext\r\nfile.csv:2: EXAMPLE1: ’Japanese’ has an invalid token: Missing a closing brace ('}')\r\nfile.csv:3: EXAMPLE2: ’Japanese’ has an invalid token: Missing an opening brace ('{')\r\nfile.csv:4: EXAMPLE3: ’English’ has an invalid token: Token containing an opening brace ('{')\r\n```\r\n\r\nなお、このInvalidTokenの診断を報告した場合は、そのフィールドはトークンが含まれないものとみなして以降の診断を続行します。\r\n\r\n### DuplicateID\r\n\r\nCSVファイルでIDの重複があると報告します。例えば、次のような文字列リソースを考えます:\r\n\r\n| ID         | `English`           | `Japanese`              |\r\n| :--        | :--                 | :--                     |\r\n| `EXAMPLE1` | `Hello {foo}`       | `こんにちは {foo}`       |\r\n| `EXAMPLE1` | `Bye {foo}`         | `さようなら {foo}`       |\r\n\r\nこのとき、次のような報告を出力します（ロケールが英語の場合）:\r\n\r\n```plaintext\r\nfile.csv:3: EXAMPLE1: This ID already appeared at line 2.\r\n```\r\n\r\n## Requirements\r\n\r\n- [.NET Core 3.1 Runtime (Runtime 3.1)][dotnet-core-runtime]\r\n\r\n## Get started\r\n\r\nPourOverは[NuGetパッケージ][pourover.globaltool]で利用可能です。次のようにインストールできます:\r\n\r\n```plaintext\r\ndotnet tool install -g PourOver.GlobalTool\r\n```\r\n\r\n## Synopsis\r\n\r\n\u003e `PourOver` [`-L` _CULTURE_] [`-hbvV`] [`--`] _FILE_.csv\r\n\r\n## Description\r\n\r\n_FILE_.csvは前述した構造のCSVファイルです。\r\n\r\nオプションは次のようになります:\r\n\r\n| | Option | | Description |\r\n|---:|:---|:---|:---|\r\n| `-L`, | `--culture` | _CULTURE_ | カルチャーを指定します（例: `en-US`） |\r\n| `-h`, | `--help` | | ヘルプメッセージを表示して終了します |\r\n| `-b`, | `--ignore-blank` | | 空欄のフィールドを無視します |\r\n| `-v`, | `--verbose` | | 出力が冗舌になります |\r\n| `-V`, | `--version` | | バージョンを出力して終了します |\r\n\r\n### Exit status\r\n\r\nPourOverは正常に終了した場合、終了ステータス0で終了します（診断の有無とは無関係です）。CSVファイルのフォーマットが壊れているなどのエラーが検出された場合は、正の整数で終了します。\r\n\r\n## How to build\r\n\r\n### Requirements to build\r\n\r\n- Visual Studio 2019 Version 16.10\r\n  or [.NET Core 3.1 SDK (SDK 3.1)][dotnet-core-sdk]\r\n\r\n### Build with .NET Core SDK\r\n\r\n```plaintext\r\ngit clone URL\r\ncd PourOver\r\ndotnet restore\r\ndotnet build\r\n```\r\n\r\n### Get test coverage report with Coverlet\r\n\r\n```plaintext\r\ndotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=opencover \\\r\n        --no-build PourOver.Test\r\ndotnet ANYWHERE/reportgenerator.dll \\\r\n        --reports:PourOver.Test/coverage.opencover.xml \\\r\n        --targetdir:Coverlet-html\r\n```\r\n\r\n### Install PourOver as a Global Tool\r\n\r\n```plaintext\r\ncd PourOver.GlobalTool\r\ndotnet pack\r\ndotnet tool install --global --add-source bin/Debug PourOver.GlobalTool\r\n```\r\n\r\n[dotnet-core-runtime]:\r\n  https://dotnet.microsoft.com/download/dotnet-core/3.1\r\n[dotnet-core-sdk]:\r\n  https://dotnet.microsoft.com/download/dotnet-core/3.1\r\n[pourover.globaltool]:\r\n  https://www.nuget.org/packages/PourOver.GlobalTool/\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaroontress%2Fpourover","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaroontress%2Fpourover","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaroontress%2Fpourover/lists"}