@johnmorrisdotca/taisen-brackets 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -2
- package/NOTICE.md +6 -1
- package/README.md +106 -9
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/layout-entry.d.ts +3 -1
- package/dist/layout-entry.js +2 -1
- package/dist/locales.d.ts +42 -0
- package/dist/locales.js +176 -0
- package/dist/matchup.d.ts +9 -6
- package/dist/matchup.js +51 -22
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/render.d.ts +6 -6
- package/dist/render.js +61 -56
- package/dist/rounds.d.ts +2 -2
- package/dist/rounds.js +4 -4
- package/dist/table.d.ts +4 -5
- package/dist/table.js +17 -13
- package/dist/theme.js +1 -1
- package/dist/types.d.ts +60 -14
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -3
- package/dist/words.d.ts +0 -7
- package/dist/words.js +0 -33
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,27 @@ All notable changes to this project are written here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
## [0.
|
|
9
|
+
## [0.2.0] - 2026-10-14
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Languages.** A `locale` option: `"en"` (the default), `"fr"`, `"ja"`, `"es"`, `"de"`, or a `Locale` made with `defineLocale`. Every text the SVG and the table write is in a table of words per language: the round names (the football terms of each language's Wikipedia article on the World Cup; the leagues' rounds as generic words, with a league's own name kept as the language uses it), the notes (after extra time, 4–2 on penalties, replay, aggregate, bye, walkover), the champion's label, the table's headers, the tooltips and the accessible description, and the numbers and dates through `Intl`. The `<svg>` and the `<table>` carry `lang`. `words` still replaces any string. Exported: `LOCALES`, `LOCALE_CODES`, `defineLocale`, `localeOf`, `voiceOf`, `localizedLayout`, `noteLong`, and the types `Locale` and `Voice`. The Spanish and German words were checked against Wikipedia only and need a native reader (docs/languages.md).
|
|
14
|
+
- **Team names in a language.** `Team.names` (by locale code), the `teamName(team, locale)` option, and `countryName(isoCode, locale)`, which asks `Intl.DisplayNames` (the home nations too), so a World Cup shows every country in the chosen language with no list written. `Team.region` carries the ISO code.
|
|
15
|
+
- A tie can be a `walkover`; a side that had a bye says so beside its score in the language.
|
|
16
|
+
- **A real example for every sport**, read from spxn.com like the others: the 2024 NBA playoffs, the playoffs of the 2024 NFL season (byes, the Super Bowl at the centre), the 2024 CFL playoffs (byes, the Grey Cup), and the 1984 Stanley Cup playoffs (the Campbell and Wales conferences, a best-of-five first round), beside the NHL 2024, MLB 2022 and the World Cups. Each has its source in NOTICE.md and the demo's footer, and a test that pins its champion and final score.
|
|
17
|
+
- **The demo** has a sport picker with its editions, a language switcher (EN, FR, 日本語, ES, DE) that drives every example, the poster included, remembered in `localStorage`, and the shared family header and footer (the family template of 2026-10-15b, `scripts/page.mjs`, the family block of the README).
|
|
18
|
+
- docs/languages.md: every term of every language, what it was checked against, and what still needs a native reader. The README has a "Languages" section and a gallery of the sports.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- The year of a format is stated: the playoffs' year, except for the NFL, whose year is its season's.
|
|
23
|
+
- The champion's German label is "Turniersieger"; the football terms in Japanese, French, Spanish and German were reviewed (French and Japanese by native-language reviewers).
|
|
24
|
+
|
|
25
|
+
## [0.1.1] - 2026-10-14
|
|
26
|
+
|
|
27
|
+
- The title is now "Taisen Brackets 対戦" in the README, the demo and the package description, matching the package name.
|
|
28
|
+
|
|
29
|
+
## [0.1.0] - 2026-10-14
|
|
10
30
|
|
|
11
31
|
First version.
|
|
12
32
|
|
|
@@ -21,5 +41,7 @@ First version.
|
|
|
21
41
|
- A demo with real results read from spxn.com (the NHL 2024 playoffs, the World Cups of 2022, 1938 and 2026, the MLB 2022 postseason; sources and terms in NOTICE.md), two made-up examples (two-legged ties, a 32-team field) and a flags-only poster of the 2026 World Cup, in a playground that changes the size, look and shape.
|
|
22
42
|
- Playoff formats checked against English Wikipedia, each era citing its articles (docs/formats.md). The year is the year the playoffs are played in.
|
|
23
43
|
|
|
24
|
-
[Unreleased]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.
|
|
44
|
+
[Unreleased]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.2.0...HEAD
|
|
45
|
+
[0.2.0]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.1.1...v0.2.0
|
|
46
|
+
[0.1.1]: https://github.com/johnmorrisdotca/taisen-brackets/compare/v0.1.0...v0.1.1
|
|
25
47
|
[0.1.0]: https://github.com/johnmorrisdotca/taisen-brackets/releases/tag/v0.1.0
|
package/NOTICE.md
CHANGED
|
@@ -7,11 +7,16 @@ The code of this package is under the MIT licence (see [LICENSE](LICENSE)). The
|
|
|
7
7
|
| `playoffFormat`: round names, series lengths, byes and teams by era | Facts (years, round names, series lengths, numbers of teams, byes) read on 2026-10-13 from English Wikipedia's articles on each playoff and tournament; the articles are cited era by era in [docs/formats.md](docs/formats.md). The MediaWiki API was read one request at a time, a second or more apart, with a User-Agent naming this project | Wikipedia's text is CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/). Only facts are taken, no sentence or table, and the articles are credited in docs/formats.md |
|
|
8
8
|
| The demo's World Cup results of 1938 and 2022 (`demo/data/worldcup-1938-2022.js`) | The Fjelstul World Cup Database, https://github.com/jfjelstul/worldcup, © 2023 Joshua C. Fjelstul, Ph.D., read through the public API of [spxn.com](https://spxn.com), which holds it as the source of those matches | **CC BY-SA 4.0** (https://creativecommons.org/licenses/by-sa/4.0/legalcode). That file is a selection of the database's fields and is shared under the same licence, with this credit. The pictures in `docs/images` that draw these results (the World Cup 2022 and 1938 brackets, and the phone pictures) are adaptations of it and carry the same licence and credit. The package, which has no data in it, is MIT |
|
|
9
9
|
| The demo's World Cup results of 2026 (`demo/data/worldcup-2026.js`) | openfootball/worldcup, https://github.com/openfootball/worldcup, read through spxn.com | CC0 |
|
|
10
|
-
| The demo's NHL results (`demo/data/nhl-2024.js`) | Every game of the 2024 Stanley Cup playoffs and each team's conference, from the NHL ("NHL", as spxn.com credits it), read through spxn.com | Facts; credited to the NHL |
|
|
10
|
+
| The demo's NHL results (`demo/data/nhl-2024.js`, `demo/data/nhl-1984.js`) | Every game of the 2024 and the 1984 Stanley Cup playoffs and each team's conference, from the NHL ("NHL", as spxn.com credits it), read through spxn.com | Facts; credited to the NHL |
|
|
11
|
+
| The demo's NBA results (`demo/data/nba-2024.js`) | Every game of the 2024 NBA playoffs and each team's conference, from English Wikipedia (https://en.wikipedia.org/wiki/2024_NBA_playoffs), read through spxn.com, which holds them as sourced from Wikipedia | **CC BY-SA 4.0** (https://creativecommons.org/licenses/by-sa/4.0/legalcode). That file is an adaptation and is shared under the same licence, with this credit; the pictures of the NBA bracket carry it too |
|
|
12
|
+
| The demo's NFL results (`demo/data/nfl-2024.js`) | Every game of the playoffs of the 2024 NFL season (January and February 2025) and each team's conference, from nflverse (https://github.com/nflverse/nflverse-data), read through spxn.com | CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/), credited to nflverse |
|
|
13
|
+
| The demo's CFL results (`demo/data/cfl-2024.js`) | Every game of the 2024 CFL playoffs and each team's division, from English Wikipedia (https://en.wikipedia.org/wiki/2024_CFL_season), read through spxn.com | **CC BY-SA 4.0**, as for the NBA file above; the pictures of the CFL bracket carry it too |
|
|
11
14
|
| The demo's MLB results (`demo/data/mlb-2022.js`) | Every game of the 2022 MLB postseason, read through spxn.com. The information used here was obtained free of charge from and is copyrighted by Retrosheet. Interested parties may contact Retrosheet at www.retrosheet.org | Retrosheet's notice, kept as it asks |
|
|
12
15
|
| How the results were read | spxn.com's public API (a PostgREST service whose `api` schema is readable with a publishable key; the key is not kept in this repository), by `scripts/fetch-demo-data.mjs`, one request at a time, a second apart, retrieved 2026-10-14 | spxn.com is the same owner's site |
|
|
13
16
|
| The demo's two-legged ties and its 32-team generic bracket | Made up for the demo: the clubs do not exist and the teams are "Team 1" to "Team 32" | Not real |
|
|
14
17
|
| The demo's national flags (in `demo/flags-data.js`) | [Hata](https://github.com/johnmorrisdotca/hata) 1.5.0 (MIT code), a development dependency used only by `scripts/make-flags.mjs`. Each flag is a Wikimedia Commons file that Hata ships only when Commons states it public domain, CC0 or CC BY; the file, author and licence of each are in `FLAG_CREDITS` in that file and in Hata's manifest and NOTICE | Each flag's own licence; none of the flags used asks for a credit. Commons marks flags as official insignia, whose use some countries restrict independently of copyright |
|
|
18
|
+
| The words of the languages (`src/locales.ts`) | The football terms (the rounds of the World Cup, extra time, the shoot-out, a replay, a walkover) were read on 2026-10-14 in the French, Spanish, German and Japanese Wikipedia articles on the World Cups of 2022, 2026 and 1938; the articles are cited in [docs/languages.md](docs/languages.md). The other words are written for this package; the French and Japanese were read by native-language reviewers, the Spanish and German were not | Wikipedia's text is CC BY-SA 4.0; only terms (single words and short phrases) are taken, no sentence or table, and the articles are credited in docs/languages.md |
|
|
19
|
+
| The names of countries in each language | Not in the package: `countryName` asks `Intl.DisplayNames`, which holds Unicode CLDR's names; the demo passes it each team's ISO code. The home nations and the two states that no longer exist are written in `src/locales.ts` and `demo/data.js` | CLDR is under the Unicode licence (https://www.unicode.org/license.txt) |
|
|
15
20
|
| The demo's page | Written for this package, with the family stylesheet `demo/family.css` | MIT |
|
|
16
21
|
|
|
17
22
|
The flags in the demo are for the nations of the World Cups of 2022 and 2026: `AR AT AU BA BE BR CA CD CH CI CO CV DE DZ EC EG ES FR GB-ENG GH HR JP KR MA MX NL NO PL PT PY SE SN US ZA`. The package itself ships no flag, no logo and no data of the leagues.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<h1 align="center">Taisen <sub>対戦</sub></h1>
|
|
1
|
+
<h1 align="center">Taisen Brackets <sub>対戦</sub></h1>
|
|
2
2
|
|
|
3
3
|
<p align="center"><strong>Knockout brackets as SVG, from a list of ties.</strong><br>
|
|
4
|
-
Give it the games, series, two-legged ties or replays of a tournament and it works out the rounds, the halves, the byes and who feeds whom, then draws the bracket: two halves that meet at the final, or one tree left to right, wide, square or tall for a phone, in light, dark or a gold-on-navy poster look, with each team's logo or round flag, the champion in the centre, and a table for readers the drawing does not serve. NHL, NBA, MLB, NFL, CFL and World Cup formats by era. One function, no dependencies.</p>
|
|
4
|
+
Give it the games, series, two-legged ties or replays of a tournament and it works out the rounds, the halves, the byes and who feeds whom, then draws the bracket: two halves that meet at the final, or one tree left to right, wide, square or tall for a phone, in light, dark or a gold-on-navy poster look, with each team's logo or round flag, the champion in the centre, and a table for readers the drawing does not serve. NHL, NBA, MLB, NFL, CFL and World Cup formats by era, in English, French, Japanese, Spanish and German. One function, no dependencies.</p>
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
7
7
|
<a href="https://github.com/johnmorrisdotca/taisen-brackets/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/johnmorrisdotca/taisen-brackets/actions/workflows/ci.yml/badge.svg"></a>
|
|
@@ -31,6 +31,28 @@ Give it the games, series, two-legged ties or replays of a tournament and it wor
|
|
|
31
31
|
</tr>
|
|
32
32
|
</table>
|
|
33
33
|
|
|
34
|
+
## Every sport
|
|
35
|
+
|
|
36
|
+
One layout draws them all, each from real results: the demo has a picker for the sport and, under it, the editions. [docs/formats.md](./docs/formats.md) has the rounds by era.
|
|
37
|
+
|
|
38
|
+
<table>
|
|
39
|
+
<tr>
|
|
40
|
+
<td align="center" valign="top"><img alt="The 2024 NHL playoffs: Western and Eastern conferences, best-of-seven series, Florida Panthers as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nhl-desk-light.png" width="360"><br><em>NHL 2024, series</em></td>
|
|
41
|
+
<td align="center" valign="top"><img alt="The 1984 Stanley Cup playoffs: the Campbell and Wales conferences, a best-of-five first round, Edmonton Oilers as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nhl1984-desk-light.png" width="360"><br><em>NHL 1984, an older era</em></td>
|
|
42
|
+
<td align="center" valign="top"><img alt="The 2024 NBA playoffs: sixteen teams in two conferences, Boston Celtics as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nba-desk-light.png" width="360"><br><em>NBA 2024</em></td>
|
|
43
|
+
</tr>
|
|
44
|
+
<tr>
|
|
45
|
+
<td align="center" valign="top"><img alt="The 2022 MLB postseason: byes for the top two seeds of each league, Houston Astros as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/byes-desk-light.png" width="360"><br><em>MLB 2022, byes</em></td>
|
|
46
|
+
<td align="center" valign="top"><img alt="The playoffs of the 2024 NFL season: single games, byes for the top seed of each conference, the Super Bowl at the centre, Philadelphia Eagles as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/nfl-desk-light.png" width="360"><br><em>NFL 2024 season, the Super Bowl</em></td>
|
|
47
|
+
<td align="center" valign="top"><img alt="The 2024 CFL playoffs: division semi-finals and finals, byes, the Grey Cup at the centre, Toronto Argonauts as champion." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/cfl-desk-light.png" width="360"><br><em>CFL 2024, the Grey Cup</em></td>
|
|
48
|
+
</tr>
|
|
49
|
+
<tr>
|
|
50
|
+
<td align="center" valign="top"><img alt="The 2022 World Cup knockout stage." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/hero-desk-light.png" width="360"><br><em>World Cup 2022</em></td>
|
|
51
|
+
<td align="center" valign="top"><img alt="The 1938 World Cup: replays." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/replays-desk-light.png" width="360"><br><em>World Cup 1938, replays</em></td>
|
|
52
|
+
<td align="center" valign="top"><img alt="The 2026 World Cup as a flags-only poster, in Japanese." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/poster-desk-ja.png" width="360"><br><em>World Cup 2026 poster, in Japanese</em></td>
|
|
53
|
+
</tr>
|
|
54
|
+
</table>
|
|
55
|
+
|
|
34
56
|
## In 30 seconds
|
|
35
57
|
|
|
36
58
|
```sh
|
|
@@ -69,6 +91,7 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
|
|
|
69
91
|
- **The champion in the centre**, with a slot for your trophy.
|
|
70
92
|
- **Accessible**: `role="img"` with a title and a description, a tooltip sentence on every tie, and `bracketTable`.
|
|
71
93
|
- **Formats by era**: NHL, NBA, MLB, NFL, CFL and FIFA World Cup round names, series lengths and byes.
|
|
94
|
+
- **Five languages**: English, French, Japanese, Spanish and German, for every word the drawing, the tooltips, the description and the table write, with the football terms of each language; countries named by `Intl` from their ISO code; numbers and dates by `Intl`. Add a language of your own.
|
|
72
95
|
- **Deterministic**: no clock, no random number, no counter; the same input gives the same string, and ids come from a hash of the input.
|
|
73
96
|
- **No dependencies**, ESM with types, about 40 kB packed.
|
|
74
97
|
|
|
@@ -274,7 +297,7 @@ console.log(bracketTable(bracket).startsWith("<table")); // true
|
|
|
274
297
|
|
|
275
298
|
### Your own words
|
|
276
299
|
|
|
277
|
-
Every word the package writes is in `WORDS
|
|
300
|
+
Every word the package writes is in `WORDS` (English; see Languages for the others); pass `words` to replace any: `{ champion: "Vainqueur", thirdPlace: "Match pour la 3e place", tbd: "À déterminer" }`. Round names come from `roundNames`: a list, or a format from `playoffFormat`.
|
|
278
301
|
|
|
279
302
|
### In React, Vue and the rest
|
|
280
303
|
|
|
@@ -291,18 +314,56 @@ The string is built from your data, with every name escaped; do not put untruste
|
|
|
291
314
|
|
|
292
315
|
Everything is exported from `@johnmorrisdotca/taisen-brackets`; the layout part from `/layout`, the formats from `/presets`. The types are in the package; each function's doc comment has its details.
|
|
293
316
|
|
|
294
|
-
- `drawBracket(input, options?)`: the SVG string. `input` is a list of matchups, `{ matchups, teams }` or a layout. Options: `size`, `orientation`, `shape`, `detail`, `theme`, `background`, `title`, `subtitle`, `roundNames`, `roundLabels`, `scores`, `seeds`, `markShape`, `champion`, `trophy`, `thirdPlace`, `winnerHighlight`, `words`, `groupOrder`, `id`, `className`.
|
|
317
|
+
- `drawBracket(input, options?)`: the SVG string. `input` is a list of matchups, `{ matchups, teams }` or a layout. Options: `size`, `orientation`, `shape`, `detail`, `theme`, `background`, `title`, `subtitle`, `roundNames`, `roundLabels`, `scores`, `seeds`, `markShape`, `champion`, `trophy`, `thirdPlace`, `winnerHighlight`, `locale`, `words`, `teamName`, `groupOrder`, `id`, `className`.
|
|
295
318
|
- `layoutBracket(input, options?)`: the rounds, halves, rows, byes and results, without drawing. `shape: "ltr"` and `groupOrder` are its options.
|
|
296
319
|
- `bracketTable(input, options?)`: the HTML table.
|
|
297
320
|
- `describeBracket(input, options?)`: the sentence a screen reader gets.
|
|
298
321
|
- `singleElimination(teams, options?)`, `advanceWinners(matchups)`, `standardSeeds(size)`: brackets for any number of teams.
|
|
299
322
|
- `matchupsFromGames(games, options)`: matchups from a flat list of games.
|
|
300
|
-
- `resolveMatchup`, `collectTeams`, `describeTie`, `noteParts`: one tie's result, the teams of a bracket, a tie in a sentence.
|
|
323
|
+
- `resolveMatchup`, `collectTeams`, `describeTie`, `noteParts`, `noteLong`: one tie's result, the teams of a bracket, a tie in a sentence.
|
|
301
324
|
- `playoffFormat(league, year)`, `playoffEras(league)`, `LEAGUES`: the formats by era (`nhl`, `nba`, `mlb`, `nfl`, `cfl`, `worldcup`).
|
|
302
325
|
- `roundName(remaining)`, `roundNamesFor(count)`, `roundLabel(layout, round, names, words)`: round names.
|
|
303
|
-
- `
|
|
326
|
+
- `LOCALES`, `LOCALE_CODES`, `defineLocale`, `localeOf`, `voiceOf`, `countryName`: the languages (see Languages), a language of your own, the formats and the names of countries from `Intl`. `localizedLayout(layout, voice)` is a layout with the team names in a language.
|
|
327
|
+
- `SIZES`, `THEMES`, `WORDS`, `VERSION`: the sizes, the looks' colours, the English words, the version.
|
|
304
328
|
- `cleanSvg(markup)`, `safeUrl(url)`, `abbreviate(name)`: the rules for marks, and the short name made from a name.
|
|
305
329
|
|
|
330
|
+
## Languages
|
|
331
|
+
|
|
332
|
+
A World Cup is read in many languages, so every text the package writes (the round names, the notes, the champion's label, the table's headers, the accessible description, the numbers and the dates) has a table of words in English, French, Japanese, Spanish and German. Pass `locale`:
|
|
333
|
+
|
|
334
|
+
```js
|
|
335
|
+
import { countryName, describeBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
336
|
+
|
|
337
|
+
const teams = [{ id: "ARG", name: "Argentina", region: "AR" }, { id: "FRA", name: "France", region: "FR" }];
|
|
338
|
+
const matchups = [{ sides: ["ARG", "FRA"], score: [3, 3], extraTime: true, pens: [4, 2], date: "2022-12-18" }];
|
|
339
|
+
// A country's name in the language comes from Intl, by its ISO code: no list of names to write.
|
|
340
|
+
const teamName = (team, locale) => team.region && countryName(team.region, locale);
|
|
341
|
+
|
|
342
|
+
for (const locale of ["en", "fr", "ja", "es", "de"]) console.log(describeBracket({ matchups, teams }, { locale, teamName }));
|
|
343
|
+
// 2 teams, 1 round. Final: Argentina 3, France 3 (after extra time, 4–2 on penalties). Winner: Argentina. Champion: Argentina.
|
|
344
|
+
// 2 équipes, 1 tour. Finale : Argentine 3, France 3 (après prolongation, 4–2 aux tirs au but). Vainqueur : Argentine. Champion : Argentine.
|
|
345
|
+
// 2チーム、1ラウンド。決勝:アルゼンチン 3–3 フランス(延長戦、PK戦 4–2)。勝者:アルゼンチン。優勝:アルゼンチン。
|
|
346
|
+
// 2 equipos, 1 ronda. Final: Argentina 3, Francia 3 (tras la prórroga, 4–2 en los penales). Ganador: Argentina. Campeón: Argentina.
|
|
347
|
+
// 2 Teams, 1 Runde. Finale: Argentinien 3, Frankreich 3 (nach Verlängerung, 4:2 im Elfmeterschießen). Sieger: Argentinien. Turniersieger: Argentinien.
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
<p align="center"><img alt="The same bracket, the World Cup of 2022 on a phone's layout, in English, French, Japanese, Spanish and German side by side: the rounds, the notes, the label under the champion and the names of the countries are each in their language." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/languages-wc2022.png" width="900"></p>
|
|
351
|
+
|
|
352
|
+
- `locale` is `"en"` (the default), `"fr"`, `"ja"`, `"es"`, `"de"`, or a `Locale` you made with `defineLocale`. It sets the words, the names of the rounds, the `lang` of the `<svg>` and of the `<table>`, the digits and the dates (`Intl`), and capitals by the language's rules (not for Japanese). `LOCALES` holds the tables and `LOCALE_CODES` lists the codes.
|
|
353
|
+
- **Round names** are the football terms of each language's Wikipedia article on the World Cup: *Huitièmes de finale*, *Octavos de final*, *Achtelfinale*, ラウンド16; the leagues' rounds (NHL, NBA, MLB, NFL, CFL) are the generic words translated, and a league's own name is kept in the form the language uses (*Finale de la Coupe Stanley*, *Stanley-Cup-Finale*, *Coupe Grey*, *Super Bowl*, スーパーボウル). [docs/languages.md](./docs/languages.md) lists every term, what it was checked against, and what still needs a native reader (Spanish and German).
|
|
354
|
+
- **Team names**: a team can carry its names (`names: { fr: "Allemagne", ja: "ドイツ" }`), the option `teamName(team, locale)` can give one, and `countryName("DE", locale)` makes a country's name from `Intl.DisplayNames` (the home nations `GB-ENG`, `GB-SCT`, `GB-WLS` and `GB-NIR` too). A club keeps its name, and a team's short name (`NED`, `ARG`) stays the FIFA code in every language.
|
|
355
|
+
- **`words`** still replaces any string of any language: `{ locale: "fr", words: { champion: "Vainqueur" } }`.
|
|
356
|
+
- **A language of your own**: `defineLocale("nl", { words: { champion: "Kampioen" }, rounds: { "Quarter-finals": "Kwartfinales" } })` fills what you leave out from English; pass the result as `locale`.
|
|
357
|
+
|
|
358
|
+
```js
|
|
359
|
+
import { defineLocale, drawBracket } from "@johnmorrisdotca/taisen-brackets";
|
|
360
|
+
|
|
361
|
+
const dutch = defineLocale("nl", { words: { champion: "Kampioen" }, rounds: { Final: "Finale" } });
|
|
362
|
+
const svg = drawBracket([{ sides: ["A", "B"], score: [2, 1] }], { locale: dutch });
|
|
363
|
+
console.log(svg.includes("KAMPIOEN")); // true
|
|
364
|
+
console.log(svg.includes('lang="nl"')); // true
|
|
365
|
+
```
|
|
366
|
+
|
|
306
367
|
## Sports covered
|
|
307
368
|
|
|
308
369
|
`playoffFormat(league, year)` gives the round names, the length of each series, the number of teams and the byes; [docs/formats.md](./docs/formats.md) lists the eras.
|
|
@@ -317,7 +378,7 @@ Everything is exported from `@johnmorrisdotca/taisen-brackets`; the layout part
|
|
|
317
378
|
| FIFA World Cup | every edition with a knockout, 1930 to 2026 (1950 had none) | Round of 32 (2026), Round of 16, Quarter-finals, Semi-finals, Final |
|
|
318
379
|
| Any other | single elimination for any number of teams | Round of N … Final |
|
|
319
380
|
|
|
320
|
-
Each era was checked against English Wikipedia (facts only; the articles are cited in [docs/formats.md](./docs/formats.md)). The year is the year the playoffs are played in
|
|
381
|
+
Each era was checked against English Wikipedia (facts only; the articles are cited in [docs/formats.md](./docs/formats.md)). The year is the year the playoffs are played in, except for the NFL, whose year is its season's, so 2024 is the season whose playoffs were played in January and February 2025. A season did something of its own (a strike, a pandemic) has its own entry, and a later one is not covered until it is checked.
|
|
321
382
|
|
|
322
383
|
## Theming
|
|
323
384
|
|
|
@@ -335,7 +396,7 @@ The names are `bg`, `ink`, `muted`, `line`, `box`, `edge`, `win`, `gold`, `plain
|
|
|
335
396
|
- Text is measured by an estimate from character classes, not a font file, so that the string is the same on every machine; a long name is cut with an ellipsis a few percent early or late.
|
|
336
397
|
- A series with no `bestOf` and no winner is not decided, whatever the games won say.
|
|
337
398
|
- On a phone the champion is at the top and the third-place match at the foot; in a wide bracket of two halves they are in the centre.
|
|
338
|
-
-
|
|
399
|
+
- The built-in languages are English, French, Japanese, Spanish and German; the Spanish and German words were checked against Wikipedia only and need a native reader (docs/languages.md). A club or a person is not translated, and a team's short name is the same in every language.
|
|
339
400
|
|
|
340
401
|
## Accessibility
|
|
341
402
|
|
|
@@ -348,7 +409,7 @@ Any current browser, and Node 22 or later. The package makes no network request
|
|
|
348
409
|
## Roadmap
|
|
349
410
|
|
|
350
411
|
- Double elimination, and round-robin groups beside a knockout.
|
|
351
|
-
-
|
|
412
|
+
- More languages (Portuguese, Italian, Korean, Chinese), and native review of the Spanish and German words.
|
|
352
413
|
- A custom element and a React helper, as in the other packages of the family.
|
|
353
414
|
- A PNG renderer (through an optional peer).
|
|
354
415
|
|
|
@@ -364,6 +425,42 @@ Libraries that draw brackets exist for several frameworks. Taisen's difference i
|
|
|
364
425
|
|
|
365
426
|
The layout began as the playoff bracket of [spxn.com](https://spxn.com), the historical record of five leagues, where one layout draws a hockey playoff and a World Cup. [docs/migrating-from-spxn.md](./docs/migrating-from-spxn.md) is how that site would use the package.
|
|
366
427
|
|
|
428
|
+
### The family
|
|
429
|
+
|
|
430
|
+
<!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
|
|
431
|
+
Taisen Brackets is one of twenty-six packages, each made for the same site, each at
|
|
432
|
+
[github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
|
|
433
|
+
|
|
434
|
+
- [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
|
|
435
|
+
- [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
|
|
436
|
+
- [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
|
|
437
|
+
- [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
|
|
438
|
+
- [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
|
|
439
|
+
- [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ): one rules engine for abstract board games, from gomoku and Reversi to Go and checkers. [Demo](https://johnmorrisdotca.github.io/narabe/).
|
|
440
|
+
- [Tenka](https://github.com/johnmorrisdotca/tenka) (天下): world conquest for two to six, on a map of the real world. [Demo](https://johnmorrisdotca.github.io/tenka/).
|
|
441
|
+
- [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
|
|
442
|
+
- [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
|
|
443
|
+
- [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
|
|
444
|
+
- [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
|
|
445
|
+
- [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
|
|
446
|
+
- [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
|
|
447
|
+
- [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
|
|
448
|
+
- [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
|
|
449
|
+
- [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮): mazes on squares, hexagons, triangles and circles, made from a seed and drawn through with a finger or the mouse. [Demo](https://johnmorrisdotca.github.io/meikyuu/).
|
|
450
|
+
- [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
|
|
451
|
+
- [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
|
|
452
|
+
- [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
|
|
453
|
+
- [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
|
|
454
|
+
- [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
|
|
455
|
+
- [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
|
|
456
|
+
- [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
|
|
457
|
+
- [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
|
|
458
|
+
- [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
|
|
459
|
+
- [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
|
|
460
|
+
|
|
461
|
+
**This package is Taisen Brackets.** The demos of all twenty-six share one header and footer, so each links the rest.
|
|
462
|
+
<!-- family:end -->
|
|
463
|
+
|
|
367
464
|
## Development
|
|
368
465
|
|
|
369
466
|
```sh
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export * from "./layout-entry.ts";
|
|
2
|
-
export { drawBracket, describeBracket, SIZES } from "./render.ts";
|
|
2
|
+
export { drawBracket, describeBracket, localizedLayout, SIZES } from "./render.ts";
|
|
3
3
|
export { bracketTable } from "./table.ts";
|
|
4
4
|
export type { TableOptions } from "./table.ts";
|
|
5
5
|
export { THEMES } from "./theme.ts";
|
|
6
6
|
export type { Palette } from "./theme.ts";
|
|
7
|
-
export { WORDS } from "./words.ts";
|
|
8
7
|
export { cleanSvg, safeUrl } from "./marks.ts";
|
|
9
8
|
export { abbreviate } from "./text.ts";
|
|
10
9
|
export { VERSION } from "./version.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
export * from "./layout-entry.js";
|
|
2
|
-
export { drawBracket, describeBracket, SIZES } from "./render.js";
|
|
2
|
+
export { drawBracket, describeBracket, localizedLayout, SIZES } from "./render.js";
|
|
3
3
|
export { bracketTable } from "./table.js";
|
|
4
4
|
export { THEMES } from "./theme.js";
|
|
5
|
-
export { WORDS } from "./words.js";
|
|
6
5
|
export { cleanSvg, safeUrl } from "./marks.js";
|
|
7
6
|
export { abbreviate } from "./text.js";
|
|
8
7
|
export { VERSION } from "./version.js";
|
package/dist/layout-entry.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from "./types.ts";
|
|
2
2
|
export { layoutBracket } from "./layout.ts";
|
|
3
|
-
export { collectTeams, resolveMatchup, describeTie, noteParts } from "./matchup.ts";
|
|
3
|
+
export { collectTeams, resolveMatchup, describeTie, noteParts, noteLong } from "./matchup.ts";
|
|
4
4
|
export { roundName, roundNamesFor, roundLabel } from "./rounds.ts";
|
|
5
5
|
export { standardSeeds, singleElimination, advanceWinners, matchupsFromGames } from "./seeding.ts";
|
|
6
6
|
export type { SingleEliminationOptions, PlayedGame, GamesOptions } from "./seeding.ts";
|
|
7
|
+
export { WORDS, LOCALES, LOCALE_CODES, defineLocale, localeOf, voiceOf, countryName } from "./locales.ts";
|
|
8
|
+
export type { Voice } from "./locales.ts";
|
package/dist/layout-entry.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./types.js";
|
|
2
2
|
export { layoutBracket } from "./layout.js";
|
|
3
|
-
export { collectTeams, resolveMatchup, describeTie, noteParts } from "./matchup.js";
|
|
3
|
+
export { collectTeams, resolveMatchup, describeTie, noteParts, noteLong } from "./matchup.js";
|
|
4
4
|
export { roundName, roundNamesFor, roundLabel } from "./rounds.js";
|
|
5
5
|
export { standardSeeds, singleElimination, advanceWinners, matchupsFromGames } from "./seeding.js";
|
|
6
|
+
export { WORDS, LOCALES, LOCALE_CODES, defineLocale, localeOf, voiceOf, countryName } from "./locales.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DrawOptions, Locale, Team, Words } from "./types.ts";
|
|
2
|
+
/** The built-in languages, by code. */
|
|
3
|
+
export declare const LOCALES: Readonly<Record<"en" | "fr" | "ja" | "es" | "de", Locale>>;
|
|
4
|
+
/** The codes of the built-in languages, English first. */
|
|
5
|
+
export declare const LOCALE_CODES: readonly string[];
|
|
6
|
+
/** The English words, as they were before there were languages. */
|
|
7
|
+
export declare const WORDS: Readonly<Words>;
|
|
8
|
+
/**
|
|
9
|
+
* A language of your own: what you give, on top of the base language's (English unless `base` says another) for what you leave out.
|
|
10
|
+
* `tag` is what `Intl` formats numbers and dates with (default `code`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function defineLocale(code: string, parts: {
|
|
13
|
+
tag?: string;
|
|
14
|
+
words?: Partial<Words>;
|
|
15
|
+
rounds?: Readonly<Record<string, string>>;
|
|
16
|
+
}, base?: keyof typeof LOCALES): Locale;
|
|
17
|
+
/** The language an option names: a code of a built-in one, or a `Locale`. */
|
|
18
|
+
export declare function localeOf(option: string | Locale | undefined): Locale;
|
|
19
|
+
/** A language ready to write with: the words with any replacements, and the formats. */
|
|
20
|
+
export interface Voice {
|
|
21
|
+
locale: Locale;
|
|
22
|
+
words: Words;
|
|
23
|
+
/** A number in the language's own digits and separators. */
|
|
24
|
+
number(n: number): string;
|
|
25
|
+
/** A date as the language writes it ("Dec 18, 2022", "2022/12/18"), or `null` when `iso` does not start with a date. */
|
|
26
|
+
date(iso: string): string | null;
|
|
27
|
+
/** Capitals, by the language's own rules (not for Japanese). */
|
|
28
|
+
upper(text: string): string;
|
|
29
|
+
/** A round's name in the language. */
|
|
30
|
+
round(name: string): string;
|
|
31
|
+
/** A team's name in the language. */
|
|
32
|
+
team(team: Team): string;
|
|
33
|
+
}
|
|
34
|
+
export declare function voiceOf(options: Pick<DrawOptions, "locale" | "words" | "teamName">): Voice;
|
|
35
|
+
/** Fills `{n}`-style holes in a phrase. */
|
|
36
|
+
export declare function fill(phrase: string, values: Record<string, string | number>): string;
|
|
37
|
+
/**
|
|
38
|
+
* The name of a country in a language, from its ISO 3166 code, through `Intl.DisplayNames`: no list to write ("DE" is
|
|
39
|
+
* Germany, Allemagne, ドイツ, Alemania, Deutschland). The home nations (`GB-ENG`, `GB-SCT`, `GB-WLS`, `GB-NIR`) are not regions
|
|
40
|
+
* to `Intl`, so they have names here. A code `Intl` does not know gives `undefined`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function countryName(code: string, locale?: string | Locale): string | undefined;
|
package/dist/locales.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The languages: English, French, Japanese, Spanish and German. A language is its words (every text a drawing and a table write),
|
|
3
|
+
* the names of the rounds by their English names, and the tag that `Intl` formats numbers and dates for. Football terms (the
|
|
4
|
+
* rounds of the World Cup, extra time, the shoot-out) were checked against each language's Wikipedia article on the 2022 World Cup
|
|
5
|
+
* (docs/languages.md); the other leagues' round names are the generic words, and a league's own proper name (the Stanley Cup, the
|
|
6
|
+
* Grey Cup, the Super Bowl) is kept in its conventional form for the language.
|
|
7
|
+
*/
|
|
8
|
+
const EN = {
|
|
9
|
+
tbd: "TBD", bye: "bye", walkover: "walkover", champion: "Champion", thirdPlace: "Third place", other: "Other ties",
|
|
10
|
+
aet: "a.e.t.", pens: "pens", aggregate: "agg.", replay: "replay",
|
|
11
|
+
aetLong: "after extra time", pensScore: "{a}–{b} on penalties", aggregateLong: "on aggregate",
|
|
12
|
+
inGames: "in {n}", bestOf: "best of {n}", round: "Round {n}", roundOf: "Round of {n}",
|
|
13
|
+
tieVs: "{a} v {b}", tieScore: "{a} {sa}, {b} {sb}", scoreText: "{a}–{b}", winnerIs: "Winner: {name}", championIs: "Champion: {name}", finalLine: "{round}: {tie}",
|
|
14
|
+
summary: "{n} teams, {r} rounds", summaryOne: "{n} teams, 1 round", sep: ". ", end: ".", note: " ({notes})", noteJoin: ", ",
|
|
15
|
+
columnRound: "Round", columnTie: "Tie", columnNote: "Note", columnDate: "Date", title: "Knockout bracket",
|
|
16
|
+
};
|
|
17
|
+
const FR = {
|
|
18
|
+
tbd: "À déterminer", bye: "exempt", walkover: "forfait", champion: "Champion", thirdPlace: "Match pour la troisième place", other: "Autres rencontres",
|
|
19
|
+
aet: "a.p.", pens: "t.a.b.", aggregate: "cumulé", replay: "match rejoué",
|
|
20
|
+
aetLong: "après prolongation", pensScore: "{a}–{b} aux tirs au but", aggregateLong: "au cumul",
|
|
21
|
+
inGames: "en {n}", bestOf: "au meilleur des {n}", round: "Tour {n}", roundOf: "Tour à {n} équipes",
|
|
22
|
+
tieVs: "{a} contre {b}", tieScore: "{a} {sa}, {b} {sb}", scoreText: "{a}–{b}", winnerIs: "Vainqueur : {name}", championIs: "Champion : {name}", finalLine: "{round} : {tie}",
|
|
23
|
+
summary: "{n} équipes, {r} tours", summaryOne: "{n} équipes, 1 tour", sep: ". ", end: ".", note: " ({notes})", noteJoin: ", ",
|
|
24
|
+
columnRound: "Tour", columnTie: "Rencontre", columnNote: "Note", columnDate: "Date", title: "Tableau à élimination directe",
|
|
25
|
+
};
|
|
26
|
+
const JA = {
|
|
27
|
+
tbd: "未定", bye: "シード", walkover: "不戦勝", champion: "優勝", thirdPlace: "3位決定戦", other: "その他の対戦",
|
|
28
|
+
aet: "延長", pens: "PK戦", aggregate: "合計", replay: "再試合",
|
|
29
|
+
aetLong: "延長戦", pensScore: "PK戦 {a}–{b}", aggregateLong: "合計スコア",
|
|
30
|
+
inGames: "{n}戦", bestOf: "{n}戦{w}勝制", round: "第{n}ラウンド", roundOf: "ラウンド{n}",
|
|
31
|
+
tieVs: "{a} 対 {b}", tieScore: "{a} {sa}–{sb} {b}", scoreText: "{a}–{b}", winnerIs: "勝者:{name}", championIs: "優勝:{name}", finalLine: "{round}:{tie}",
|
|
32
|
+
summary: "{n}チーム、{r}ラウンド", summaryOne: "{n}チーム、1ラウンド", sep: "。", end: "。", note: "({notes})", noteJoin: "、",
|
|
33
|
+
columnRound: "ラウンド", columnTie: "対戦", columnNote: "備考", columnDate: "日付", title: "トーナメント表",
|
|
34
|
+
};
|
|
35
|
+
const ES = {
|
|
36
|
+
tbd: "Por definir", bye: "exento", walkover: "incomparecencia", champion: "Campeón", thirdPlace: "Partido por el tercer puesto", other: "Otros partidos",
|
|
37
|
+
aet: "prórroga", pens: "penales", aggregate: "global", replay: "desempate",
|
|
38
|
+
aetLong: "tras la prórroga", pensScore: "{a}–{b} en los penales", aggregateLong: "en el global",
|
|
39
|
+
inGames: "en {n}", bestOf: "al mejor de {n}", round: "Ronda {n}", roundOf: "Ronda de {n}",
|
|
40
|
+
tieVs: "{a} vs. {b}", tieScore: "{a} {sa}, {b} {sb}", scoreText: "{a}–{b}", winnerIs: "Ganador: {name}", championIs: "Campeón: {name}", finalLine: "{round}: {tie}",
|
|
41
|
+
summary: "{n} equipos, {r} rondas", summaryOne: "{n} equipos, 1 ronda", sep: ". ", end: ".", note: " ({notes})", noteJoin: ", ",
|
|
42
|
+
columnRound: "Ronda", columnTie: "Partido", columnNote: "Nota", columnDate: "Fecha", title: "Cuadro de eliminatorias",
|
|
43
|
+
};
|
|
44
|
+
const DE = {
|
|
45
|
+
tbd: "offen", bye: "Freilos", walkover: "kampflos", champion: "Turniersieger", thirdPlace: "Spiel um Platz 3", other: "Weitere Begegnungen",
|
|
46
|
+
aet: "n. V.", pens: "i. E.", aggregate: "ges.", replay: "Wiederholungsspiel",
|
|
47
|
+
aetLong: "nach Verlängerung", pensScore: "{a}:{b} im Elfmeterschießen", aggregateLong: "im Gesamtergebnis",
|
|
48
|
+
inGames: "in {n}", bestOf: "Best of {n}", round: "Runde {n}", roundOf: "Runde der letzten {n}",
|
|
49
|
+
tieVs: "{a} gegen {b}", tieScore: "{a} {sa}, {b} {sb}", scoreText: "{a}:{b}", winnerIs: "Sieger: {name}", championIs: "Turniersieger: {name}", finalLine: "{round}: {tie}",
|
|
50
|
+
summary: "{n} Teams, {r} Runden", summaryOne: "{n} Teams, 1 Runde", sep: ". ", end: ".", note: " ({notes})", noteJoin: ", ",
|
|
51
|
+
columnRound: "Runde", columnTie: "Begegnung", columnNote: "Hinweis", columnDate: "Datum", title: "Turnierbaum",
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The round names, English name first. The football rounds are the terms of each language's Wikipedia article on the World Cup;
|
|
55
|
+
* the leagues' rounds are the generic words (first round, semi-finals), and the league's own name is kept as the language writes it.
|
|
56
|
+
*/
|
|
57
|
+
const ROUNDS = {
|
|
58
|
+
"Round of 32": ["Seizièmes de finale", "ラウンド32", "Dieciseisavos de final", "Sechzehntelfinale"],
|
|
59
|
+
"Round of 16": ["Huitièmes de finale", "ラウンド16", "Octavos de final", "Achtelfinale"],
|
|
60
|
+
"Quarter-finals": ["Quarts de finale", "準々決勝", "Cuartos de final", "Viertelfinale"],
|
|
61
|
+
"Quarterfinals": ["Quarts de finale", "準々決勝", "Cuartos de final", "Viertelfinale"],
|
|
62
|
+
"Semi-finals": ["Demi-finales", "準決勝", "Semifinales", "Halbfinale"],
|
|
63
|
+
"Semifinals": ["Demi-finales", "準決勝", "Semifinales", "Halbfinale"],
|
|
64
|
+
"Final": ["Finale", "決勝", "Final", "Finale"],
|
|
65
|
+
"Preliminary Round": ["Tour préliminaire", "予選ラウンド", "Ronda preliminar", "Vorrunde"],
|
|
66
|
+
"Qualifying Round": ["Tour de qualification", "予選ラウンド", "Ronda de clasificación", "Qualifikationsrunde"],
|
|
67
|
+
"First Round": ["Premier tour", "1回戦", "Primera ronda", "Erste Runde"],
|
|
68
|
+
"Second Round": ["Deuxième tour", "2回戦", "Segunda ronda", "Zweite Runde"],
|
|
69
|
+
"Division Semifinals": ["Demi-finales de division", "ディビジョン準決勝", "Semifinales de división", "Division-Halbfinale"],
|
|
70
|
+
"Division Finals": ["Finales de division", "ディビジョン決勝", "Finales de división", "Division-Finale"],
|
|
71
|
+
"Conference Quarterfinals": ["Quarts de finale de conférence", "カンファレンス準々決勝", "Cuartos de final de conferencia", "Conference-Viertelfinale"],
|
|
72
|
+
"Conference Semifinals": ["Demi-finales de conférence", "カンファレンス準決勝", "Semifinales de conferencia", "Conference-Halbfinale"],
|
|
73
|
+
"Conference Finals": ["Finales de conférence", "カンファレンス決勝", "Finales de conferencia", "Conference-Finale"],
|
|
74
|
+
"Stanley Cup Semifinals": ["Demi-finales de la Coupe Stanley", "スタンレー・カップ準決勝", "Semifinales de la Stanley Cup", "Stanley-Cup-Halbfinale"],
|
|
75
|
+
"Stanley Cup Final": ["Finale de la Coupe Stanley", "スタンレー・カップ・ファイナル", "Final de la Stanley Cup", "Stanley-Cup-Finale"],
|
|
76
|
+
"NBA Finals": ["Finale de la NBA", "NBAファイナル", "Finales de la NBA", "NBA-Finale"],
|
|
77
|
+
"Wild Card Game": ["Match de wild card", "ワイルドカードゲーム", "Juego de comodín", "Wild-Card-Spiel"],
|
|
78
|
+
"Wild Card Series": ["Série de wild card", "ワイルドカードシリーズ", "Serie de comodín", "Wild-Card-Serie"],
|
|
79
|
+
"Division Series": ["Série de division", "ディビジョンシリーズ", "Serie divisional", "Division Series"],
|
|
80
|
+
"League Championship Series": ["Série de championnat", "リーグチャンピオンシップシリーズ", "Serie de Campeonato de Liga", "League Championship Series"],
|
|
81
|
+
"World Series": ["Série mondiale", "ワールドシリーズ", "Serie Mundial", "World Series"],
|
|
82
|
+
"Wild Card": ["Wild card", "ワイルドカード・プレーオフ", "Comodín", "Wild-Card-Runde"],
|
|
83
|
+
"Divisional": ["Ronde divisionnaire", "ディビジョナル・プレーオフ", "Ronda divisional", "Divisional Round"],
|
|
84
|
+
"Divisional Playoffs": ["Ronde divisionnaire", "ディビジョナル・プレーオフ", "Ronda divisional", "Divisional Round"],
|
|
85
|
+
"Conference Championship": ["Finale de conférence", "カンファレンス・チャンピオンシップ", "Campeonato de conferencia", "Conference Championship"],
|
|
86
|
+
"Conference Championships": ["Finales de conférence", "カンファレンス・チャンピオンシップ", "Campeonatos de conferencia", "Conference Championships"],
|
|
87
|
+
"Super Bowl": ["Super Bowl", "スーパーボウル", "Super Bowl", "Super Bowl"],
|
|
88
|
+
"Division Semi-Final": ["Demi-finale de division", "ディビジョン準決勝", "Semifinal de división", "Division-Halbfinale"],
|
|
89
|
+
"Division Final": ["Finale de division", "ディビジョン決勝", "Final de división", "Division-Finale"],
|
|
90
|
+
"Grey Cup": ["Coupe Grey", "グレイ・カップ", "Grey Cup", "Grey Cup"],
|
|
91
|
+
};
|
|
92
|
+
const rounds = (index) => Object.fromEntries(Object.entries(ROUNDS).map(([en, names]) => [en, names[index]]));
|
|
93
|
+
/** The built-in languages, by code. */
|
|
94
|
+
export const LOCALES = Object.freeze({
|
|
95
|
+
en: { code: "en", tag: "en", words: EN, rounds: {} },
|
|
96
|
+
fr: { code: "fr", tag: "fr", words: FR, rounds: rounds(0) },
|
|
97
|
+
ja: { code: "ja", tag: "ja", words: JA, rounds: rounds(1) },
|
|
98
|
+
es: { code: "es", tag: "es", words: ES, rounds: rounds(2) },
|
|
99
|
+
de: { code: "de", tag: "de", words: DE, rounds: rounds(3) },
|
|
100
|
+
});
|
|
101
|
+
/** The codes of the built-in languages, English first. */
|
|
102
|
+
export const LOCALE_CODES = Object.freeze(["en", "fr", "ja", "es", "de"]);
|
|
103
|
+
/** The English words, as they were before there were languages. */
|
|
104
|
+
export const WORDS = LOCALES.en.words;
|
|
105
|
+
/**
|
|
106
|
+
* A language of your own: what you give, on top of the base language's (English unless `base` says another) for what you leave out.
|
|
107
|
+
* `tag` is what `Intl` formats numbers and dates with (default `code`).
|
|
108
|
+
*/
|
|
109
|
+
export function defineLocale(code, parts, base = "en") {
|
|
110
|
+
const from = LOCALES[base];
|
|
111
|
+
return { code, tag: parts.tag ?? code, words: { ...from.words, ...parts.words }, rounds: { ...from.rounds, ...parts.rounds } };
|
|
112
|
+
}
|
|
113
|
+
/** The language an option names: a code of a built-in one, or a `Locale`. */
|
|
114
|
+
export function localeOf(option) {
|
|
115
|
+
if (option === undefined)
|
|
116
|
+
return LOCALES.en;
|
|
117
|
+
if (typeof option !== "string")
|
|
118
|
+
return option;
|
|
119
|
+
const hit = LOCALES[option] ?? LOCALES[option.split("-")[0]];
|
|
120
|
+
if (!hit)
|
|
121
|
+
throw new Error(`taisen-brackets: no language ${JSON.stringify(option)}; the built-in ones are ${LOCALE_CODES.join(", ")} (defineLocale makes another)`);
|
|
122
|
+
return hit;
|
|
123
|
+
}
|
|
124
|
+
export function voiceOf(options) {
|
|
125
|
+
const locale = localeOf(options.locale);
|
|
126
|
+
const words = options.words ? { ...locale.words, ...options.words } : locale.words;
|
|
127
|
+
const numbers = new Intl.NumberFormat(locale.tag);
|
|
128
|
+
const dates = new Intl.DateTimeFormat(locale.tag, { dateStyle: "medium", timeZone: "UTC" });
|
|
129
|
+
return {
|
|
130
|
+
locale,
|
|
131
|
+
words,
|
|
132
|
+
number: (n) => numbers.format(n),
|
|
133
|
+
date: (iso) => {
|
|
134
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
|
|
135
|
+
return m ? dates.format(new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])))) : null;
|
|
136
|
+
},
|
|
137
|
+
upper: (text) => (locale.code === "ja" ? text : text.toLocaleUpperCase(locale.tag)),
|
|
138
|
+
round: (name) => {
|
|
139
|
+
const hit = locale.rounds[name];
|
|
140
|
+
if (hit !== undefined)
|
|
141
|
+
return hit;
|
|
142
|
+
const of = /^Round of (\d+)$/.exec(name);
|
|
143
|
+
return of ? fill(words.roundOf, { n: of[1] }) : name;
|
|
144
|
+
},
|
|
145
|
+
team: (team) => options.teamName?.(team, locale) ?? team.names?.[locale.code] ?? team.name,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** Fills `{n}`-style holes in a phrase. */
|
|
149
|
+
export function fill(phrase, values) {
|
|
150
|
+
return phrase.replace(/\{(\w+)\}/g, (hole, key) => (key in values ? String(values[key]) : hole));
|
|
151
|
+
}
|
|
152
|
+
const SUBDIVISIONS = {
|
|
153
|
+
"GB-ENG": { en: "England", fr: "Angleterre", ja: "イングランド", es: "Inglaterra", de: "England" },
|
|
154
|
+
"GB-SCT": { en: "Scotland", fr: "Écosse", ja: "スコットランド", es: "Escocia", de: "Schottland" },
|
|
155
|
+
"GB-WLS": { en: "Wales", fr: "Pays de Galles", ja: "ウェールズ", es: "Gales", de: "Wales" },
|
|
156
|
+
"GB-NIR": { en: "Northern Ireland", fr: "Irlande du Nord", ja: "北アイルランド", es: "Irlanda del Norte", de: "Nordirland" },
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* The name of a country in a language, from its ISO 3166 code, through `Intl.DisplayNames`: no list to write ("DE" is
|
|
160
|
+
* Germany, Allemagne, ドイツ, Alemania, Deutschland). The home nations (`GB-ENG`, `GB-SCT`, `GB-WLS`, `GB-NIR`) are not regions
|
|
161
|
+
* to `Intl`, so they have names here. A code `Intl` does not know gives `undefined`.
|
|
162
|
+
*/
|
|
163
|
+
export function countryName(code, locale = "en") {
|
|
164
|
+
const l = localeOf(locale);
|
|
165
|
+
const sub = SUBDIVISIONS[code.toUpperCase()];
|
|
166
|
+
if (sub)
|
|
167
|
+
return sub[l.code] ?? sub.en;
|
|
168
|
+
try {
|
|
169
|
+
const names = new Intl.DisplayNames([l.tag], { type: "region" });
|
|
170
|
+
const name = names.of(code.toUpperCase());
|
|
171
|
+
return name && name !== code.toUpperCase() ? name : undefined;
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
package/dist/matchup.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { Matchup, SideId, Team, Tie
|
|
1
|
+
import type { Matchup, SideId, Team, Tie } from "./types.ts";
|
|
2
|
+
import { type Voice } from "./locales.ts";
|
|
2
3
|
/** A bracket's teams: the list given, plus a team made from every id that a matchup names and the list does not. */
|
|
3
4
|
export declare function collectTeams(matchups: readonly Matchup[], given?: readonly Team[]): Map<SideId, Team>;
|
|
4
5
|
/** One matchup as a tie: sides resolved to teams, the winner and the result worked out. The round is set by the layout. */
|
|
5
6
|
export declare function resolveMatchup(m: Matchup, index: number, teams: ReadonlyMap<SideId, Team>): Tie;
|
|
6
7
|
/** The short name of a team for a small box. */
|
|
7
|
-
export declare const abbrOf: (team: Team | null,
|
|
8
|
-
/** The pieces of a note under a tie's score: "a.e.t.", "pens", "in 7", "agg.". */
|
|
9
|
-
export declare function noteParts(tie: Tie,
|
|
10
|
-
/**
|
|
11
|
-
export declare function
|
|
8
|
+
export declare const abbrOf: (team: Team | null, v: Voice) => string;
|
|
9
|
+
/** The pieces of a note under a tie's score, short: "a.e.t.", "pens", "in 7", "agg.". */
|
|
10
|
+
export declare function noteParts(tie: Tie, v: Voice): string[];
|
|
11
|
+
/** The notes of a tie written out, for a sentence or a table: "after extra time", "4–2 on penalties". */
|
|
12
|
+
export declare function noteLong(tie: Tie, v: Voice): string[];
|
|
13
|
+
/** A tie in a sentence, for a tooltip, a table and a description: "Argentina 3, France 3 (after extra time, 4–2 on penalties). Winner: Argentina". */
|
|
14
|
+
export declare function describeTie(tie: Tie, v: Voice): string;
|