├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── index.html ├── package-lock.json ├── package.json └── spec.emu /.gitattributes: -------------------------------------------------------------------------------- 1 | index.html -diff merge=ours 2 | spec.js -diff merge=ours 3 | spec.css -diff merge=ours 4 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Deploy spec 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v1 12 | with: 13 | node-version: '12.x' 14 | - run: npm install 15 | - run: npm run build 16 | - name: commit changes 17 | uses: elstudio/actions-js-build/commit@v3 18 | with: 19 | commitMessage: "fixup: [spec] `npm run build`" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | pnpm-lock.yaml 44 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ECMA TC39 and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Intl.LocaleMatcher` 2 | 3 | ## Motivation 4 | 5 | i18n-supported websites often get a list of preferred locales via `Accept-Language` header or `navigator.languages`. They then try to determine the best available locale based on the set of locales that they support (and have translations for). 6 | 7 | This operation currently exists within ECMA-402 but is only available as an abstract operation. Surfacing this functionality as a top level API would improve locale negotiation correctness and developer productivity as sites will be able to reliably handle not only matching, but also aliases, fallbacks and such. 8 | 9 | ### Use cases 10 | 11 | 1. Given a set of locales an application has translations for and the set of locales a user requests, [find the best matching locales](https://github.com/vercel/next.js/issues/18676). 12 | 2. JS runtimes (& polyfills) are not required to guarantee supporting all locales. Given a set of locales it supports and what the user requests, [find the best matching locales](https://github.com/formatjs/formatjs/discussions/2669). 13 | 3. An application can also provide [different "tones"](https://github.com/formatjs/formatjs/discussions/2642) of the same locales (e.g casual, formal), utilizing `-x-` private tag. Given a set of locales with extensions and what the user preference might be, find the best matching locales. 14 | 15 | ## Status 16 | 17 | **Stage 1** 18 | 19 | Ponyfill: https://formatjs.io/docs/polyfills/intl-localematcher 20 | 21 | ## Champion 22 | 23 | - [Long Ho (@longlho)](https://github.com/longlho) 24 | 25 | ## API 26 | 27 | ```ts 28 | interface Options { 29 | algorithm: 'lookup' | 'best fit' 30 | } 31 | 32 | Intl.LocaleMatcher.match( 33 | requestedLocales: string[], 34 | availableLocales: string[], 35 | defaultLocale: string, 36 | options?: Options 37 | ): string 38 | ``` 39 | 40 | ### Options 41 | 42 | 1. `lookup` would continue to be the existing `LookupMatcher` implementation within ECMA-402. 43 | 1. `best fit` would be implementation-dependent. 44 | 45 | ## Examples 46 | 47 | ```ts 48 | Intl.LocaleMatcher.match(["fr-XX", "en"], ["fr", "en"], "en"); // 'fr' 49 | ``` 50 | 51 | ## Prior Arts 52 | 53 | ### [@hapi/accept](https://github.com/hapijs/accept) 54 | 55 | This is the core of hapijs header parsing with quality preferences. This however does a naive hierarchy with exact matches only. For example: 56 | 57 | ```js 58 | Accept.language("en;q=0.7, fr-XX;q=0.8", ["fr", "en"]); // language === "en" 59 | ``` 60 | 61 | which would not be accurate. 62 | 63 | ### [koa](https://koajs.com/#request) 64 | 65 | Similarly, Koa's `request.acceptsLanguages` follow similar exact match algorithm. 66 | 67 | ### [UTS35 LanguageMatching](https://www.unicode.org/reports/tr35/tr35.html#LanguageMatching) 68 | 69 | This details a more sophisticated locale negotiation algorithm that is more accurate than `hapi`/`koa` 70 | 71 | ### [RFC4647 Section 3.4](https://tools.ietf.org/html/rfc4647#section-3.4) 72 | 73 | This is the `lookup` algorithm in ECMA-402. 74 | 75 | ### [cldrjs's lookup implementation](https://github.com/rxaviers/cldrjs/blob/master/doc/bundle_lookup_matcher.md#implementation-details) 76 | 77 | Similar to UTS35 LanguageMatching. 78 | 79 | ## References 80 | 81 | - https://github.com/tc39/ecma402/issues/513 82 | - https://github.com/tc39/ecma402/issues/46 83 | - https://github.com/vercel/next.js/issues/18676 84 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 |This is an algorithm:
1859 |© 2021 Your Name(s) Here
1865 | 1866 |All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.
1868 | 1869 |Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1870 | 1871 |THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1878 | 1879 |7 | title: Proposal Title Goes Here 8 | stage: -1 9 | contributors: Your Name(s) Here 10 |11 | 12 |
This is an algorithm:
15 |