language package
diff --git a/internal/go_repository_tools_srcs.bzl b/internal/go_repository_tools_srcs.bzl
index da90ef0..9dac61f 100644
--- a/internal/go_repository_tools_srcs.bzl
+++ b/internal/go_repository_tools_srcs.bzl
@@ -156,6 +156,8 @@
     Label("//v2/internal/wspace:finder.go"),
     Label("//v2/label:BUILD.bazel"),
     Label("//v2/label:label.go"),
+    Label("//v2/language:BUILD.bazel"),
+    Label("//v2/language:lang.go"),
     Label("//v2/merger:BUILD.bazel"),
     Label("//v2/merger:fix.go"),
     Label("//v2/merger:merger.go"),
diff --git a/language/BUILD.bazel b/language/BUILD.bazel
index 77e2950..d680f66 100644
--- a/language/BUILD.bazel
+++ b/language/BUILD.bazel
@@ -12,10 +12,10 @@
     visibility = ["//visibility:public"],
     deps = [
         "//config",
-        "//label",
         "//repo",
         "//resolve",
-        "//rule",
+        "//v2/label",
+        "//v2/rule",
     ],
 )
 
diff --git a/language/base.go b/language/base.go
index bc2d8db..5107975 100644
--- a/language/base.go
+++ b/language/base.go
@@ -18,11 +18,11 @@
 import (
 	"flag"
 
+	"github.com/bazel-contrib/bazel-gazelle/v2/label"
+	"github.com/bazel-contrib/bazel-gazelle/v2/rule"
 	"github.com/bazelbuild/bazel-gazelle/config"
-	"github.com/bazelbuild/bazel-gazelle/label"
 	"github.com/bazelbuild/bazel-gazelle/repo"
 	"github.com/bazelbuild/bazel-gazelle/resolve"
-	"github.com/bazelbuild/bazel-gazelle/rule"
 )
 
 // BaseLang implements the minimum of language.Language interface.
diff --git a/language/lang.go b/language/lang.go
index da8d81e..7673e31 100644
--- a/language/lang.go
+++ b/language/lang.go
@@ -22,9 +22,9 @@
 package language
 
 import (
+	"github.com/bazel-contrib/bazel-gazelle/v2/rule"
 	"github.com/bazelbuild/bazel-gazelle/config"
 	"github.com/bazelbuild/bazel-gazelle/resolve"
-	"github.com/bazelbuild/bazel-gazelle/rule"
 )
 
 // Language describes an extension for Gazelle that provides support for
@@ -60,6 +60,9 @@
 //
 // * Merging generated rules into existing rules: languages provide metadata
 // that helps with rule matching, merging, and deletion.
+//
+// Deprecated: use github.com/bazel-contrib/bazel-gazelle/v2/language.Language
+// or other smaller v2 interfaces instead.
 type Language interface {
 	// TODO(jayconrod): is embedding Configurer strictly necessary?
 	config.Configurer
diff --git a/language/update.go b/language/update.go
index cebf73a..b42d955 100644
--- a/language/update.go
+++ b/language/update.go
@@ -16,9 +16,9 @@
 package language
 
 import (
+	"github.com/bazel-contrib/bazel-gazelle/v2/rule"
 	"github.com/bazelbuild/bazel-gazelle/config"
 	"github.com/bazelbuild/bazel-gazelle/repo"
-	"github.com/bazelbuild/bazel-gazelle/rule"
 )
 
 // RepoUpdater may be implemented by languages that support updating
diff --git a/v2/BUILD.bazel b/v2/BUILD.bazel
index 392c64f..1db2421 100644
--- a/v2/BUILD.bazel
+++ b/v2/BUILD.bazel
@@ -12,6 +12,7 @@
         "//v2/flag:all_files",
         "//v2/internal:all_files",
         "//v2/label:all_files",
+        "//v2/language:all_files",
         "//v2/merger:all_files",
         "//v2/pathtools:all_files",
         "//v2/resolve:all_files",
diff --git a/v2/language/BUILD.bazel b/v2/language/BUILD.bazel
new file mode 100644
index 0000000..49c6c6d
--- /dev/null
+++ b/v2/language/BUILD.bazel
@@ -0,0 +1,22 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "language",
+    srcs = ["lang.go"],
+    importpath = "github.com/bazel-contrib/bazel-gazelle/v2/language",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//v2/config",
+        "//v2/rule",
+    ],
+)
+
+filegroup(
+    name = "all_files",
+    testonly = True,
+    srcs = [
+        "BUILD.bazel",
+        "lang.go",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/v2/language/lang.go b/v2/language/lang.go
new file mode 100644
index 0000000..0304b3b
--- /dev/null
+++ b/v2/language/lang.go
@@ -0,0 +1,163 @@
+/* Copyright 2018 The Bazel Authors. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package language provides interfaces for language extensions in Gazelle.
+//
+// To implement a new extension, define a type that implements [Language]
+// and other interfaces listed here. You will likely also want to implement
+// [github.com/bazel-contrib/bazel-gazelle/v2/config.Configurer],
+// [github.com/bazel-contrib/bazel-gazelle/v2/resolve.Indexer], and
+// [github.com/bazel-contrib/bazel-gazelle/v2/resolve.Resolver]. Then define
+// a function `NewV2()` that returns a value of your type. `gazelle_binary`
+// generates code that calls this function when Gazelle starts.
+//
+// These interfaces are designd for extensibility. Most interfaces are optional,
+// and most have only one method. Most methods accept arguments and return
+// results as struct values, so more fields may be added in the future without
+// breaking compatibility.
+package language
+
+import (
+	"context"
+
+	"github.com/bazel-contrib/bazel-gazelle/v2/config"
+	"github.com/bazel-contrib/bazel-gazelle/v2/rule"
+)
+
+// Language is a marker interface for extensions. It is the only mandatory
+// interface that extensions must implement.
+type Language interface {
+	// Name returns the name of the extension, like "go" or "cc". Name must always
+	// return the same value.
+	Name() string
+}
+
+// Generator has methods for generating new rules.
+//
+// Most extensions that implement Generator should also implement
+// [github.com/bazel-contrib/bazel-gazelle/v2/resolve.Indexer] and
+// [github.com/bazel-contrib/bazel-gazelle/v2/resolve.Resolver] to support
+// dependency resolution. But this is not necessary if generated rules'
+// dependencies can be completely determined without indexing other rules
+// in the repo.
+type Generator interface {
+	// Kinds returns a map from rule names to information on how to match and
+	// merge attributes that may be found in rules of those kinds. All kinds of
+	// rules generated for this language may be found here.
+	Kinds() map[string]rule.KindInfo
+
+	// Generate reads source files in a directory and produces a list of rules
+	// that should appear in that directory's build file.
+	Generate(context.Context, GenerateArgs) (GenerateResult, error)
+}
+
+type GenerateArgs struct {
+	// Config is the configuration for the current directory.
+	Config *config.Config
+
+	// Dir is the canonical absolute path to the directory.
+	Dir string
+
+	// Rel is the slash-separated path to the directory, relative to the
+	// repository root ("" for the root directory itself). This may be used
+	// as the package name in labels.
+	Rel string
+
+	// File is the build file for the directory. File is nil if there is
+	// no existing build file.
+	File *rule.File
+
+	// Subdirs is a list of subdirectories in the directory, including
+	// symbolic links to directories that Gazelle will follow.
+	// RegularFiles is a list of regular files including other symbolic
+	// links.
+	// GenFiles is a list of generated files in the directory
+	// (usually these are mentioned as "out" or "outs" attributes in rules).
+	// These slices must not be modified.
+	Subdirs, RegularFiles, GenFiles []string
+
+	// OtherEmpty is a list of empty rules generated by other languages.
+	// OtherGen is a list of generated rules generated by other languages.
+	OtherEmpty, OtherGen []*rule.Rule
+}
+
+type GenerateResult struct {
+	// Gen is a list of rules generated from files found in the directory
+	// GenerateRules was asked to process. These will be merged with existing
+	// rules or added to the build file.
+	Gen []*rule.Rule
+
+	// Empty is a list of rules that cannot be built with the files found in the
+	// directory GenerateRules was asked to process. These will be merged with
+	// existing rules. If the merged rules are empty, they will be deleted.
+	Empty []*rule.Rule
+
+	// Imports contains information about the imported libraries for each
+	// rule in Gen. Gen and Imports must have the same length, since they
+	// correspond. These values are passed to Resolve after merge. The type
+	// is opaque since different languages may use different representations.
+	//
+	// TODO(v2): make this optional or eliminate it
+	Imports []any
+
+	// RelsToIndex is a list of additional directories to index for dependency
+	// resolution, expressed as slash-separated paths relative to the repository
+	// root, or "" for the root directory itself. If indexing is enabled,
+	// libraries in these directories are indexed before dependencies are
+	// resolved. Subdirectories are not recursively indexed. This list may
+	// contain non-existent directories.
+	RelsToIndex []string
+}
+
+// Fixer supports repairing deprecated usage of rules in a BUILD file.
+// Fix is called in a directory Gazelle will update after the BUILD file is
+// parsed, before new rules are generated and before rules are indexed.
+// Fix should only perform destructive changes (like deleting or renaming rules)
+// if `args.Config.ShouldFix` is true.
+type Fixer interface {
+	Fix(context.Context, FixArgs) error
+}
+
+type FixArgs struct {
+	// Config is the configuration for the current directory.
+	Config *config.Config
+
+	// Rel is the slash-separated path to the directory, relative to the
+	// repository root ("" for the root directory itself). This may be used
+	// as the package name in labels.
+	Rel string
+
+	// File is the build file for the directory. File is nil if there is
+	// no existing build file.
+	File *rule.File
+}
+
+type OnStarter interface {
+	// Called when Gazelle starts, before walking the directory tree and before
+	// Configure is called on any extension.
+	OnStart(context.Context) error
+}
+
+type OnResolver interface {
+	// Called after Gazelle generates rules in all directories it will visit
+	// but before Gazelle performs dependency resolution.
+	OnResolve(context.Context) error
+}
+
+type OnFinisher interface {
+	// Called after Gazelle has resolved dependencies on all rules and written
+	// updated build files.
+	OnFinish(context.Context) error
+}