diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 55d9460..5c1ed3f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,12 +6,12 @@ jobs:
   build:
     name: Build
     runs-on: ubuntu-latest
-    if: (github.event_name == 'push' && github.repository_owner == 'nicksnyder') || (github.event_name == 'pull_request' && github.repository_owner != 'nicksnyder')
+    if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'pull_request'
     steps:
       - name: Install Go
         uses: actions/setup-go@v2
         with:
-          go-version: 1.15.2
+          go-version: 1.17.7
       - name: Git checkout
         uses: actions/checkout@v2
       - name: Build
@@ -25,15 +25,15 @@ jobs:
         run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
       - name: Upload coverage
         uses: codecov/codecov-action@v1
-  build_1_9_7:
-    name: Build with Go 1.9.7
+  build_1_12:
+    name: Build with Go 1.12.17
     runs-on: ubuntu-latest
-    if: (github.event_name == 'push' && github.repository_owner == 'nicksnyder') || (github.event_name == 'pull_request' && github.repository_owner != 'nicksnyder')
+    if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'pull_request'
     steps:
       - name: Install Go
         uses: actions/setup-go@v2
         with:
-          go-version: 1.9.7
+          go-version: 1.12.17
       - name: Git checkout
         uses: actions/checkout@v2
         with:
diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml
index a4da704..cbd187d 100644
--- a/.github/workflows/goreleaser.yml
+++ b/.github/workflows/goreleaser.yml
@@ -18,7 +18,7 @@ jobs:
         name: Set up Go
         uses: actions/setup-go@v2
         with:
-          go-version: '^1.15.2'
+          go-version: '^1.17.7'
       -
         name: Release
         uses: goreleaser/goreleaser-action@v2
diff --git a/.github/workflows/lsif-go.yml b/.github/workflows/lsif-go.yml
index 6ab74e0..7e56f79 100644
--- a/.github/workflows/lsif-go.yml
+++ b/.github/workflows/lsif-go.yml
@@ -10,5 +10,7 @@ jobs:
       - uses: actions/checkout@v1
       - name: Generate LSIF data
         run: lsif-go
+        working-directory: v2
       - name: Upload LSIF data to Sourcegraph.com
         run: src lsif upload -github-token=${{ secrets.GITHUB_TOKEN }} -ignore-upload-failure
+        working-directory: v2
diff --git a/README.md b/README.md
index d292ea3..a13f268 100644
--- a/README.md
+++ b/README.md
@@ -2,12 +2,13 @@
 
 go-i18n is a Go [package](#package-i18n) and a [command](#command-goi18n) that helps you translate Go programs into multiple languages.
 
-- Supports [pluralized strings](http://cldr.unicode.org/index/cldr-spec/plural-rules) for all 200+ languages in the [Unicode Common Locale Data Repository (CLDR)](http://www.unicode.org/cldr/charts/28/supplemental/language_plural_rules.html).
-  - Code and tests are [automatically generated](https://github.com/nicksnyder/go-i18n/tree/master/i18n/language/codegen) from [CLDR data](http://cldr.unicode.org/index/downloads).
+- Supports [pluralized strings](http://cldr.unicode.org/index/cldr-spec/plural-rules) for all 200+ languages in the [Unicode Common Locale Data Repository (CLDR)](https://www.unicode.org/cldr/charts/28/supplemental/language_plural_rules.html).
+  - Code and tests are [automatically generated](https://github.com/nicksnyder/go-i18n/tree/main/v2/internal/plural/codegen) from [CLDR data](http://cldr.unicode.org/index/downloads).
 - Supports strings with named variables using [text/template](http://golang.org/pkg/text/template/) syntax.
 - Supports message files of any format (e.g. JSON, TOML, YAML).
 
-## Package i18n [![GoDoc](http://godoc.org/github.com/nicksnyder/go-i18n?status.svg)](http://godoc.org/github.com/nicksnyder/go-i18n/v2/i18n)
+## Package i18n
+[![GoDoc](https://godoc.org/github.com/nicksnyder/go-i18n?status.svg)](https://godoc.org/github.com/nicksnyder/go-i18n/v2/i18n)
 
 The i18n package provides support for looking up messages according to a set of locale preferences.
 
@@ -55,7 +56,8 @@ localizer.Localize(&i18n.LocalizeConfig{
 }) // Nick has 2 cats.
 ```
 
-## Command goi18n [![GoDoc](http://godoc.org/github.com/nicksnyder/go-i18n?status.svg)](http://godoc.org/github.com/nicksnyder/go-i18n/v2/goi18n)
+## Command goi18n
+[![GoDoc](https://godoc.org/github.com/nicksnyder/go-i18n?status.svg)](https://godoc.org/github.com/nicksnyder/go-i18n/v2/goi18n)
 
 The goi18n command manages message files used by the i18n package.
 
@@ -115,9 +117,9 @@ If you have added new messages to your program:
 
 ## For more information and examples:
 
-- Read the [documentation](http://godoc.org/github.com/nicksnyder/go-i18n/v2).
-- Look at the [code examples](https://github.com/nicksnyder/go-i18n/blob/master/v2/i18n/example_test.go) and [tests](https://github.com/nicksnyder/go-i18n/blob/master/v2/i18n/localizer_test.go).
-- Look at an example [application](https://github.com/nicksnyder/go-i18n/tree/master/v2/example).
+- Read the [documentation](https://godoc.org/github.com/nicksnyder/go-i18n/v2).
+- Look at the [code examples](https://github.com/nicksnyder/go-i18n/blob/main/v2/i18n/example_test.go) and [tests](https://github.com/nicksnyder/go-i18n/blob/main/v2/i18n/localizer_test.go).
+- Look at an example [application](https://github.com/nicksnyder/go-i18n/tree/main/v2/example).
 
 ## License
 
diff --git a/debian/changelog b/debian/changelog
index dff649d..3c95caa 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+golang-github-nicksnyder-go-i18n.v2 (2.2.0+git20220312.1.639caa7-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+  * Drop patch 0001-Update-test-to-new-error-message-in-go-1.16-260.patch,
+    present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 17 Apr 2022 00:15:24 -0000
+
 golang-github-nicksnyder-go-i18n.v2 (2.1.2-1) unstable; urgency=medium
 
   * New upstream version 2.1.2
diff --git a/debian/patches/0001-Update-test-to-new-error-message-in-go-1.16-260.patch b/debian/patches/0001-Update-test-to-new-error-message-in-go-1.16-260.patch
deleted file mode 100644
index dd36e3a..0000000
--- a/debian/patches/0001-Update-test-to-new-error-message-in-go-1.16-260.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From dadad2bdb2a713748df0717eddd54495150cfb58 Mon Sep 17 00:00:00 2001
-From: William Wilson <william.wilson@canonical.com>
-Date: Wed, 23 Jun 2021 18:37:51 -0500
-Subject: [PATCH] Update test to new error message in go 1.16 (#260)
-
----
- v2/internal/template_test.go | 7 ++++---
- 1 file changed, 4 insertions(+), 3 deletions(-)
-
-diff --git a/v2/internal/template_test.go b/v2/internal/template_test.go
-index 2f5d991..4f569b9 100644
---- a/v2/internal/template_test.go
-+++ b/v2/internal/template_test.go
-@@ -1,6 +1,7 @@
- package internal
- 
- import (
-+	"strings"
- 	"testing"
- 	"text/template"
- )
-@@ -45,7 +46,7 @@ func TestExecute(t *testing.T) {
- 			template: &Template{
- 				Src: "hello {{",
- 			},
--			err:      "template: :1: unexpected unclosed action in command",
-+			err:      "unclosed action",
- 			noallocs: true,
- 		},
- 	}
-@@ -53,8 +54,8 @@ func TestExecute(t *testing.T) {
- 	for _, test := range tests {
- 		t.Run(test.template.Src, func(t *testing.T) {
- 			result, err := test.template.Execute(test.funcs, test.data)
--			if actual := str(err); actual != test.err {
--				t.Errorf("expected err %q; got %q", test.err, actual)
-+			if actual := str(err); !strings.Contains(str(err), test.err) {
-+				t.Errorf("expected err %q to contain %q", actual, test.err)
- 			}
- 			if result != test.result {
- 				t.Errorf("expected result %q; got %q", test.result, result)
--- 
-2.34.0
-
diff --git a/debian/patches/series b/debian/patches/series
index 736f6e2..e69de29 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +0,0 @@
-0001-Update-test-to-new-error-message-in-go-1.16-260.patch
diff --git a/v2/go.mod b/v2/go.mod
index 447a685..fb36943 100644
--- a/v2/go.mod
+++ b/v2/go.mod
@@ -1,9 +1,9 @@
 module github.com/nicksnyder/go-i18n/v2
 
-go 1.9
+go 1.12
 
 require (
-	github.com/BurntSushi/toml v0.3.1
-	golang.org/x/text v0.3.3
+	github.com/BurntSushi/toml v1.0.0
+	golang.org/x/text v0.3.7
 	gopkg.in/yaml.v2 v2.3.0
 )
diff --git a/v2/go.sum b/v2/go.sum
index 96bf3d7..06161a6 100644
--- a/v2/go.sum
+++ b/v2/go.sum
@@ -1,7 +1,7 @@
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
+github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/v2/i18n/bundlefs.go b/v2/i18n/bundlefs.go
new file mode 100644
index 0000000..50c794b
--- /dev/null
+++ b/v2/i18n/bundlefs.go
@@ -0,0 +1,18 @@
+// +build go1.16
+
+package i18n
+
+import (
+	"io/fs"
+)
+
+// LoadMessageFileFS is like LoadMessageFile but instead of reading from the
+// hosts operating system's file system it reads from the fs file system.
+func (b *Bundle) LoadMessageFileFS(fsys fs.FS, path string) (*MessageFile, error) {
+	buf, err := fs.ReadFile(fsys, path)
+	if err != nil {
+		return nil, err
+	}
+
+	return b.ParseMessageFileBytes(buf, path)
+}
diff --git a/v2/i18n/localizer.go b/v2/i18n/localizer.go
index 17261e2..256cd49 100644
--- a/v2/i18n/localizer.go
+++ b/v2/i18n/localizer.go
@@ -9,6 +9,15 @@ import (
 )
 
 // Localizer provides Localize and MustLocalize methods that return localized messages.
+// Localize and MustLocalize methods use a language.Tag matching algorithm based
+// on the best possible value. This algorithm may cause an unexpected language.Tag returned
+// value depending on the order of the tags stored in memory. For example, if the bundle
+// used to create a Localizer instance ingested locales following this order
+// ["en-US", "en-GB", "en-IE", "en"] and the locale "en" is asked, the underlying matching
+// algorithm will return "en-US" thinking it is the best match possible. More information
+// about the algorithm in this Github issue: https://github.com/golang/go/issues/49176.
+// There is additionnal informations inside the Go code base:
+// https://github.com/golang/text/blob/master/language/match.go#L142
 type Localizer struct {
 	// bundle contains the messages that can be returned by the Localizer.
 	bundle *Bundle
diff --git a/v2/internal/plural/codegen/README.md b/v2/internal/plural/codegen/README.md
index 41a0094..f1f5ff8 100644
--- a/v2/internal/plural/codegen/README.md
+++ b/v2/internal/plural/codegen/README.md
@@ -1,6 +1,6 @@
 # How to upgrade CLDR data
 
 1.  Go to http://cldr.unicode.org/index/downloads to find the latest version.
-1.  Download the latest version of cldr-common (e.g. http://unicode.org/Public/cldr/37/cldr-common-37.0.zip)
+1.  Download the latest version of cldr-common (e.g. https://unicode.org/Public/cldr/39/cldr-common-39.0.zip)
 1.  Unzip and copy `common/supplemental/plurals.xml` to this directory.
 1.  Run `generate.sh`.
diff --git a/v2/internal/template_test.go b/v2/internal/template_test.go
index 2f5d991..4f569b9 100644
--- a/v2/internal/template_test.go
+++ b/v2/internal/template_test.go
@@ -1,6 +1,7 @@
 package internal
 
 import (
+	"strings"
 	"testing"
 	"text/template"
 )
@@ -45,7 +46,7 @@ func TestExecute(t *testing.T) {
 			template: &Template{
 				Src: "hello {{",
 			},
-			err:      "template: :1: unexpected unclosed action in command",
+			err:      "unclosed action",
 			noallocs: true,
 		},
 	}
@@ -53,8 +54,8 @@ func TestExecute(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.template.Src, func(t *testing.T) {
 			result, err := test.template.Execute(test.funcs, test.data)
-			if actual := str(err); actual != test.err {
-				t.Errorf("expected err %q; got %q", test.err, actual)
+			if actual := str(err); !strings.Contains(str(err), test.err) {
+				t.Errorf("expected err %q to contain %q", actual, test.err)
 			}
 			if result != test.result {
 				t.Errorf("expected result %q; got %q", test.result, result)