diff --git a/Library/Homebrew/cask/audit.rb b/Library/Homebrew/cask/audit.rb
index 05b01042ba33bd83cba552749a01cb2d18f77f60..f5cd9c2315e8274e580832ddf7700d1ef707407c 100644
--- a/Library/Homebrew/cask/audit.rb
+++ b/Library/Homebrew/cask/audit.rb
@@ -330,14 +330,11 @@ module Cask
     end
 
     def check_languages
-      invalid = []
       @cask.languages.each do |language|
-        invalid << language.to_s unless language.match?(/^[a-z]{2}$/) || language.match?(/^[a-z]{2}-[A-Z]{2}$/)
+        Locale.parse(language)
+      rescue Locale::ParserError
+        add_error "Locale '#{language}' is invalid."
       end
-
-      return if invalid.empty?
-
-      add_error "locale #{invalid.join(", ")} are invalid"
     end
 
     def check_token_conflicts
diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb
index 3f5ec39412c5201e6d84ac7ad996f0782781ffea..585b93ebb9110ba928be69d4f4b842315b84e6f7 100644
--- a/Library/Homebrew/cask/dsl.rb
+++ b/Library/Homebrew/cask/dsl.rb
@@ -136,7 +136,7 @@ module Cask
     end
 
     def language_eval
-      return @language if instance_variable_defined?(:@language)
+      return @language if defined?(@language)
 
       return @language = nil if @language_blocks.nil? || @language_blocks.empty?
 
diff --git a/Library/Homebrew/locale.rb b/Library/Homebrew/locale.rb
index 174c594ad90b7813124972070becf263f3735002..c858aa03b5d2281e8c4ad699943f5f4750783944 100644
--- a/Library/Homebrew/locale.rb
+++ b/Library/Homebrew/locale.rb
@@ -1,27 +1,56 @@
 # frozen_string_literal: true
 
+# Representation of a system locale.
+#
+# Used to compare the system language and languages defined using cask `language` stanza.
+#
+# @api private
 class Locale
+  # Error when a string cannot be parsed to a `Locale`.
   class ParserError < StandardError
   end
 
-  LANGUAGE_REGEX = /(?:[a-z]{2,3})/.freeze     # ISO 639-1 or ISO 639-2
-  REGION_REGEX   = /(?:[A-Z]{2}|\d{3})/.freeze # ISO 3166-1 or UN M.49
-  SCRIPT_REGEX   = /(?:[A-Z][a-z]{3})/.freeze  # ISO 15924
+  # ISO 639-1 or ISO 639-2
+  LANGUAGE_REGEX = /(?:[a-z]{2,3})/.freeze
+  private_constant :LANGUAGE_REGEX
+
+  # ISO 3166-1 or UN M.49
+  REGION_REGEX = /(?:[A-Z]{2}|\d{3})/.freeze
+  private_constant :REGION_REGEX
+
+  # ISO 15924
+  SCRIPT_REGEX = /(?:[A-Z][a-z]{3})/.freeze
+  private_constant :SCRIPT_REGEX
 
   LOCALE_REGEX = /\A((?:#{LANGUAGE_REGEX}|#{REGION_REGEX}|#{SCRIPT_REGEX})(?:-|$)){1,3}\Z/.freeze
+  private_constant :LOCALE_REGEX
 
   def self.parse(string)
-    string = string.to_s
+    if locale = try_parse(string)
+      return locale
+    end
+
+    raise ParserError, "'#{string}' cannot be parsed to a #{self}"
+  end
+
+  def self.try_parse(string)
+    return if string.blank?
 
-    raise ParserError, "'#{string}' cannot be parsed to a #{self}" unless string.match?(LOCALE_REGEX)
+    scanner = StringScanner.new(string)
 
-    scan = proc do |regex|
-      string.scan(/(?:-|^)(#{regex})(?:-|$)/).flatten.first
+    if language = scanner.scan(LANGUAGE_REGEX)
+      sep = scanner.scan(/-/)
+      return if (sep && scanner.eos?) || (sep.nil? && !scanner.eos?)
     end
 
-    language = scan.call(LANGUAGE_REGEX)
-    region   = scan.call(REGION_REGEX)
-    script   = scan.call(SCRIPT_REGEX)
+    if region = scanner.scan(REGION_REGEX)
+      sep = scanner.scan(/-/)
+      return if (sep && scanner.eos?) || (sep.nil? && !scanner.eos?)
+    end
+
+    script = scanner.scan(SCRIPT_REGEX)
+
+    return unless scanner.eos?
 
     new(language, region, script)
   end
@@ -46,7 +75,10 @@ class Locale
   end
 
   def include?(other)
-    other = self.class.parse(other) unless other.is_a?(self.class)
+    unless other.is_a?(self.class)
+      other = self.class.try_parse(other)
+      return false if other.nil?
+    end
 
     [:language, :region, :script].all? do |var|
       if other.public_send(var).nil?
@@ -58,12 +90,14 @@ class Locale
   end
 
   def eql?(other)
-    other = self.class.parse(other) unless other.is_a?(self.class)
+    unless other.is_a?(self.class)
+      other = self.class.try_parse(other)
+      return false if other.nil?
+    end
+
     [:language, :region, :script].all? do |var|
       public_send(var) == other.public_send(var)
     end
-  rescue ParserError
-    false
   end
   alias == eql?
 
diff --git a/Library/Homebrew/test/cask/audit_spec.rb b/Library/Homebrew/test/cask/audit_spec.rb
index b96b89faf2c8af404bf303a12adf6af972a54f6d..c39a09590a497181172efc300f1979a40058707f 100644
--- a/Library/Homebrew/test/cask/audit_spec.rb
+++ b/Library/Homebrew/test/cask/audit_spec.rb
@@ -278,7 +278,6 @@ describe Cask::Audit, :cask do
     end
 
     describe "locale validation" do
-      let(:strict) { true }
       let(:cask) do
         tmp_cask "locale-cask-test", <<~RUBY
           cask 'locale-cask-test' do
@@ -318,7 +317,9 @@ describe Cask::Audit, :cask do
 
       context "when cask locale is invalid" do
         it "error with invalid locale" do
-          expect(subject).to fail_with(/locale ZH-CN, zh-, zh-cn are invalid/)
+          expect(subject).to fail_with(/Locale 'ZH-CN' is invalid\./)
+          expect(subject).to fail_with(/Locale 'zh-' is invalid\./)
+          expect(subject).to fail_with(/Locale 'zh-cn' is invalid\./)
         end
       end
     end
diff --git a/Library/Homebrew/test/locale_spec.rb b/Library/Homebrew/test/locale_spec.rb
index f7618613487bcbabc6cab9122400ed45ab326bb6..52c0e6aeb6a32c4a3ec7a11d32ab79fa8dc31e25 100644
--- a/Library/Homebrew/test/locale_spec.rb
+++ b/Library/Homebrew/test/locale_spec.rb
@@ -26,6 +26,9 @@ describe Locale do
         expect { described_class.parse("zh-CN_Hans") }.to raise_error(Locale::ParserError)
         expect { described_class.parse("zhCN") }.to raise_error(Locale::ParserError)
         expect { described_class.parse("zh_Hans") }.to raise_error(Locale::ParserError)
+        expect { described_class.parse("zh-") }.to raise_error(Locale::ParserError)
+        expect { described_class.parse("ZH-CN") }.to raise_error(Locale::ParserError)
+        expect { described_class.parse("zh-cn") }.to raise_error(Locale::ParserError)
       end
     end
   end