diff --git a/.github/workflows/language-check.yml b/.github/workflows/language-check.yml index a5134399..68c51063 100644 --- a/.github/workflows/language-check.yml +++ b/.github/workflows/language-check.yml @@ -1,46 +1,24 @@ -name: Check README Suffixes +name: Validate README Navigation on: pull_request: paths: - - 'README-*' + - 'README*.md' + - 'scripts/validate_readmes.rb' + - '.github/workflows/language-check.yml' jobs: - check-readme-suffixes: + validate-readmes: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.0 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' - - name: Install dependencies - run: | - gem install nokogiri - - - name: Check README suffixes - run: | - # Scrape ISO 639 language codes from the Wiki page - wget -qO- https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes | \ - nokogiri -e 'puts $_.css(".wikitable td:nth-child(2)").map(&:text)' | \ - sed -nr '/^.{0,2}$/p' | # Grab only two letter values - tr '[:lower:]' '[:upper:]' > iso639.txt # Covert letters to uppercase - - # Get list of README file names - readme_suffixes=$(ls README-* | sed 's/README-//g; s/.md//g') - - # Check if README suffixes are valid ISO 639 language codes - for suffix in $readme_suffixes; do - if ! grep -qw "$suffix" iso639.txt; then - echo "Error: Invalid README suffix found: $suffix" - exit 1 - fi - done - - echo "All README suffixes are valid ISO 639 language codes." - - # Add more steps for your existing workflow, e.g., running tests or other checks + - name: Validate README links and translation index + run: ruby scripts/validate_readmes.rb diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3bac4264..da01e911 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,10 +28,7 @@ jobs: continue-on-error: true run: | gem install awesome_bot - awesome_bot \ - -f README*.md \ - --allow 302,429 \ - --white-list https://www.firsttimersonly.com/,https://github.com/github,https://creativecommons.org/licenses/by-sa/4.0/,https://github.com/kentcdodds + awesome_bot -f README*.md --allow 302,429 --white-list https://www.firsttimersonly.com/,https://github.com/github,https://creativecommons.org/licenses/by-sa/4.0/,https://github.com/kentcdodds # - name: Run Danger # uses: MeilCli/danger-action@v5 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b9138fc..cb4ebf61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,9 +164,12 @@ The main `README.md` file is written in English. That file will be the template This repository is about contributing to open source and generally, translation is important to reach diverse audiences. It is recommended that you provide language-specific resources links instead of the English-resource links. -The non-English README files are named `README-XX.md`, where `xx` is the +The non-English README files are named `README-XX.md`, where `XX` usually +follows the [two letter language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) -for the language. +for the language. Some existing translations use established variants such as +`pt-BR`, so follow the naming convention already used in the repository when +you update or add localized files. You can contribute to the non-English README files by taking links that are in the English README but are not in the language of your choosing. When making a pull request with these changes, please tag someone who can verify your language contribution by adding `@` in front of their GitHub username to the pull request. @@ -174,6 +177,8 @@ If your language does not exist yet, feel free to start it yourself. If you deci [create an issue](https://github.com/freeCodeCamp/how-to-contribute-to-open-source/issues/new/choose) to crowdsource help. +When you add a new translation, also add it to the language list at the top of the main `README.md` so contributors can discover it easily. The README workflow validates that README-to-README links point to existing files and that every translation is represented in the main language picker. + ### Adding to the Project File We have a diff --git a/PROJECTS.md b/PROJECTS.md index f443b1f8..c5f07288 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -4,11 +4,11 @@ This file contains a list of projects and organizations that are friendly to contributions, along with quick links to relevant documents you should reference before contributing. -- `[Babel](https://github.com/babel/babel)` (project) +- [Babel](https://github.com/babel/babel) (project) - [Contributing Guide](https://github.com/babel/babel/blob/master/CONTRIBUTING.md) - Issue labels: - [Good First Issue](https://github.com/babel/babel/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) -- `[BeeWare](https://beeware.org/)` (org) +- [BeeWare](https://beeware.org/) (org) - [Contributing Guide](https://beeware.org/contributing/how/first-time/) - [Batavia](https://github.com/beeware/batavia) (project) - Issue labels: @@ -16,35 +16,35 @@ before contributing. - [VOC](https://github.com/beeware/voc) (project) - Issue labels: - [first-timers-only](https://github.com/beeware/voc/issues?q=is%3Aopen+is%3Aissue+label%3Afirst-timers-only) -- `[DITA Open Toolkit](https://www.dita-ot.org/)` +- [DITA Open Toolkit](https://www.dita-ot.org/) - [Contributing Guide](https://www.dita-ot.org/contributing) - Issue label: - [good first issue](https://github.com/dita-ot/dita-ot/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) -- `[Docker](https://github.com/docker)` (project) +- [Docker](https://github.com/docker) (project) - [Contributing Guide](https://docs.docker.com/opensource/) - Issue labels: - [Open Issue](https://github.com/search?q=org%3Adocker+is%3Aissue+is%3Aopen) - [exp/beginner](https://github.com/docker/docker/issues?q=is%3Aopen+is%3Aissue+label%3Aexp%2Fbeginner+sort%3Aupdated-desc) -- `[freeCodeCamp.org](https://www.freecodecamp.org/)` (org) +- [freeCodeCamp.org](https://www.freecodecamp.org/) (org) - [freeCodeCamp.org](https://github.com/freeCodeCamp/freeCodeCamp/) (project) - [Contributing guide](https://contribute.freecodecamp.org) - Issue labels: - [first-timers-only](https://github.com/FreeCodeCamp/FreeCodeCamp/issues?q=is%3Aopen+is%3Aissue+label%3Afirst-timers-only) - [help wanted](https://github.com/freeCodeCamp/freeCodeCamp/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) -- `[GitLab](https://gitlab.com/gitlab-org)` (org) +- [GitLab](https://gitlab.com/gitlab-org) (org) - [Contributing Guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) - [Development Guides](https://docs.gitlab.com/ce/development/README.html) - [Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) - Issue labels: - [1st contribution](https://gitlab.com/gitlab-org/gitlab-ce/issues?scope=all&utf8=%E2%9C%93&state=opened&label_name[]=1st%20contribution) -- `[GNOME](https://www.gnome.org/)` (org) +- [GNOME](https://www.gnome.org/) (org) - [Contributing Guide](https://wiki.gnome.org/Newcomers) - Great big example application (org) - [Great-big-example-application](https://github.com/dancancro/great-big-angular2-example) (project) - [Contributing Guide](https://github.com/dancancro/great-big-example-application/projects/1) -- `[Hoa](https://hoa-project.net)` (project) +- [Hoa](https://hoa-project.net) (project) - [Contributing Guide](https://hoa-project.net/En/Literature/Contributor/Guide.html) -- `[Hoodiehq](http://hood.ie/)` (org) +- [Hoodiehq](http://hood.ie/) (org) - [Camp](https://github.com/hoodiehq/camp) (project) - Issue labels: - [first-timers-only](https://github.com/hoodiehq/camp/labels/first-timers-only) @@ -55,77 +55,77 @@ before contributing. - [first-timers-only](https://github.com/hoodiehq/hoodie/labels/first-timers-only) - [help wanted](https://github.com/hoodiehq/hoodie/labels/help%20wanted) - [starter](https://github.com/hoodiehq/hoodie/labels/starter) -- `[Metamaps](https://metamaps.cc/)` (org) +- [Metamaps](https://metamaps.cc/) (org) - [Metamaps](https://github.com/metamaps/metamaps) (project) - [Contribution Guide](https://github.com/metamaps/metamaps/blob/develop/doc/CONTRIBUTING.md) -- `nteract` (org) +- nteract (org) - [nteract](https://github.com/nteract/nteract) (project) - [Contributing Guide](https://github.com/nteract/nteract/blob/master/CONTRIBUTING.md) - Issue labels: - [new-contributor-friendly](https://github.com/nteract/nteract/issues?q=is%3Aissue+label%3Anew-contributor-friendly+is%3Aopen) - [mentoring-available](https://github.com/nteract/nteract/issues?q=is%3Aissue+is%3Aopen+label%3Amentoring-available) -- `Public Lab` (org) +- Public Lab (org) - [Plots2](https://github.com/publiclab/plots2) (project) - [Contributing guide](https://github.com/publiclab/plots2/blob/master/CONTRIBUTING.md) - Issue labels: - [first-timers-only](https://github.com/publiclab/plots2/issues?q=is%3Aissue+is%3Aopen+label%3Afirst-timers-only) - [help-wanted](https://github.com/publiclab/plots2/issues?q=is%3Aissue+is%3Aopen+label%3Ahelp-wanted) - [fto-candidate](https://github.com/publiclab/plots2/issues?q=is%3Aissue+is%3Aopen+label%3Afto-candidate) -- `React` (org) +- React (org) - [React](https://github.com/facebook/react) (project) - [Contributing Guide](https://reactjs.org/docs/how-to-contribute.html) - Issue labels: - [good first issue](https://github.com/facebook/react/issues?page=1&q=is%3Aissue+is%3Aopen) -- `Read the Docs` (org) +- Read the Docs (org) - [Read the Docs](http://docs.readthedocs.io/en/latest/index.html) (site) - Issue labels: - [Good First Bug](https://github.com/rtfd/readthedocs.org/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+First+Bug%22) -- `Redfin` (org) +- Redfin (org) - [react-server](https://github.com/redfin/react-server) (project) - [Contributing Guide](https://github.com/redfin/react-server/blob/master/CONTRIBUTING.md) - Issue labels: - [good first contribution](https://github.com/redfin/react-server/labels/good%20first%20contribution) -- `Servo` (org) +- Servo (org) - [Servo](https://starters.servo.org/) (project) - [Contributing guide](https://github.com/servo/servo/blob/master/CONTRIBUTING.md) - Issue labels: - [Easy](https://github.com/servo/servo/issues?q=is%3Aissue+is%3Aopen+label%3AE-easy) -- `[Sympy](https://github.com/sympy/sympy)` (project) +- [Sympy](https://github.com/sympy/sympy) (project) - [Dev Wiki](https://github.com/sympy/sympy/wiki#development) - [Work Flow](https://github.com/sympy/sympy/wiki/Development-workflow) - [Contributing Guide](https://github.com/sympy/sympy/wiki/Introduction-to-contributing) - Issue labels: - [Easy to fix](https://github.com/sympy/sympy/issues?q=is%3Aissue+is%3Aopen+label%3A%22Easy+to+Fix%22) -- `TEAMMATES` (org) +- TEAMMATES (org) - [TEAMMATES](https://github.com/TEAMMATES/teammates) (project) - [Contributing Guide](https://github.com/TEAMMATES/teammates/blob/master/docs/CONTRIBUTING.md) - Issue labels: - [d.FirstTimers](https://github.com/TEAMMATES/teammates/issues?q=is%3Aopen+is%3Aissue+label%3Ad.FirstTimers) - [d.Contributors](https://github.com/TEAMMATES/teammates/issues?q=is%3Aopen+is%3Aissue+label%3Ad.Contributors) -- `Zulip` (org) +- Zulip (org) - [Zulip](https://github.com/zulip) (project) - [Contributing Guide](https://github.com/zulip/zulip/blob/master/CONTRIBUTING.md) - Issue labels: - [Good First Issue](https://github.com/zulip/zulip/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) - [Help Wanted](https://github.com/zulip/zulip/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) -- `[Rust](https://github.com/rust-lang/rust)` (project) +- [Rust](https://github.com/rust-lang/rust) (project) - [Contributing Guide](https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md) - Issue labels: - [E-easy](https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AE-easy) -- `[Test Kitchen](https://github.com/test-kitchen/test-kitchen)` (project) +- [Test Kitchen](https://github.com/test-kitchen/test-kitchen) (project) - [Contributing Guide](https://github.com/test-kitchen/test-kitchen/blob/master/CONTRIBUTING.md) - Issue labels: - [*](https://github.com/test-kitchen/test-kitchen/issues?q=is%3Aopen+is%3Aissue+label%3A%E2%AD%90%EF%B8%8F) -- `Django` (Framework) +- Django (Framework) - [Contributing Guide](https://github.com/django/django) -- `[tsParticles](https://github.com/matteobruni/tsparticles)` (Library) +- [tsParticles](https://github.com/matteobruni/tsparticles) (Library) - [Contributing Guide](https://github.com/matteobruni/tsparticles/blob/master/CONTRIBUTING.md) - Issue labels: - [Good First Issue](https://github.com/matteobruni/tsparticles/labels/good%20first%20issue) - [Help Wanted](https://github.com/matteobruni/tsparticles/labels/help%20wanted) -- `[Jenkins](https://github.com/jenkinsci/jenkins)` (Project) +- [Jenkins](https://github.com/jenkinsci/jenkins) (Project) - [Contributing Guide](https://wiki.jenkins.io/display/JENKINS/Beginners+Guide+to+Contributing) -- `[ifme](https://github.com/ifmeorg/ifme)` (Project) +- [ifme](https://github.com/ifmeorg/ifme) (Project) - [Contributing Guide](https://github.com/ifmeorg/ifme/blob/main/CONTRIBUTING.md) - Issue labels: - [first-timers-only](https://github.com/ifmeorg/ifme/issues?q=is%3Aopen+is%3Aissue+label%3Afirst-timers-only) diff --git a/README-VI.md b/README-VI.md index 04de7c64..d5b53f95 100644 --- a/README-VI.md +++ b/README-VI.md @@ -18,7 +18,7 @@
  • Français
  • 한국어
  • 日本語
  • -
  • Tiếng Việt
  • +
  • Tiếng Việt
  • diff --git a/README.md b/README.md index dcd5d9b1..1ad1757a 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,20 @@
  • فارسی
  • اردو
  • اللغة العربية
  • +
  • български
  • +
  • ગુજરાતી
  • +
  • Armenian
  • +
  • Bahasa Melayu
  • +
  • Nederlands
  • +
  • Polski
  • +
  • संस्कृतम्
  • +
  • සිංහල
  • +
  • Slovenščina
  • தமிழ்
  • +
  • తెలుగు
  • नेपाली
  • +
  • Українська
  • +
  • Tiếng Việt
  • മലയാളം
  • diff --git a/scripts/validate_readmes.rb b/scripts/validate_readmes.rb new file mode 100644 index 00000000..e18b5749 --- /dev/null +++ b/scripts/validate_readmes.rb @@ -0,0 +1,38 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +root = File.expand_path("..", __dir__) +readme_paths = Dir.glob(File.join(root, "README*.md")).sort +readme_files = readme_paths.map { |path| File.basename(path) } +translation_files = readme_files.grep(/^README-.+\.md$/).sort +readme_link_pattern = /href="\.\/(README[^"]+\.md)"/ + +errors = [] + +readme_paths.each do |path| + basename = File.basename(path) + content = File.read(path) + linked_files = content.scan(readme_link_pattern).flatten + missing_targets = linked_files.reject { |target| readme_files.include?(target) }.uniq + + next if missing_targets.empty? + + errors << "#{basename} links to missing README files: #{missing_targets.join(', ')}" +end + +main_readme_path = File.join(root, "README.md") +main_readme_links = File.read(main_readme_path).scan(readme_link_pattern).flatten.uniq +missing_from_main = translation_files.reject { |target| main_readme_links.include?(target) } + +unless missing_from_main.empty? + errors << "README.md is missing language-picker entries for: #{missing_from_main.join(', ')}" +end + +if errors.empty? + puts "README validation passed for #{readme_files.length} files." + exit 0 +end + +warn "README validation failed:" +errors.each { |error| warn "- #{error}" } +exit 1